package org.jsola.hr.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.domain.AccDetailModel;
import com.alipay.api.response.AlipayFundBatchDetailQueryResponse;
import com.alipay.api.response.AlipayFundBatchUniTransferResponse;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.alipay.api.response.AlipayUserAgreementQueryResponse;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.entity.ContentType;
import org.joda.time.DateTime;
import org.jsola.approval.dto.NewApproDTO;
import org.jsola.approval.query.UserFlowApproQuery;
import org.jsola.approval.vo.UserApproNodeInfoVO;
import org.jsola.approval.vo.UserApproVO;
import org.jsola.cache.ICache;
import org.jsola.common.*;
import org.jsola.contract.core.MockMultipartFile;
import org.jsola.core.Page;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.CacheKeyKit;
import org.jsola.hr.common.ExampleKit;
import org.jsola.hr.common.ImageUtil;
import org.jsola.hr.common.StreamKit;
import org.jsola.hr.common.oneKeyPay.AliSafetyPay;
import org.jsola.hr.common.oneKeyPay.Alipay;
import org.jsola.hr.common.oneKeyPay.PingAnBankPay;
import org.jsola.hr.common.oneKeyPay.VXpayClear;
import org.jsola.hr.constant.*;
import org.jsola.hr.core.HrOneKeyPayProperties;
import org.jsola.hr.dao.ITransferAccountsBatchDAO;
import org.jsola.hr.dao.ITransferAccountsZfbBatchDAO;
import org.jsola.hr.dto.*;
import org.jsola.hr.entity.*;
import org.jsola.hr.provider.*;
import org.jsola.hr.query.*;
import org.jsola.hr.service.*;
import org.jsola.hr.vo.*;
import org.jsola.hr.vo.payAli.*;
import org.jsola.hr.vo.payAli.safetyPay.*;
import org.jsola.hr.vo.payPingAn.*;
import org.jsola.hr.vo.payWx.*;
import org.jsola.json.JsonKit;
import org.jsola.log.common.LogKit;
import org.jsola.message.dto.ShortMessageDTO;
import org.jsola.user.core.TokenUser;
import org.jsola.user.entity.UserDO;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import tk.mybatis.mapper.entity.Example;

import java.io.*;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 转账批次
 *
 * @author zhr
 */
@Slf4j
@Service("hrTransferAccountsBatchServiceImpl")
public class TransferAccountsBatchServiceImpl implements ITransferAccountsBatchService {

    @Autowired
    private ITransferAccountsBatchDAO transferAccountsBatchDAO;
    @Autowired
    private ITransferAccountsDetailService transferAccountsDetailService;
    @Autowired
    private ITransferAccountsZfbBatchService transferAccountsZfbBatchService;
    @Autowired
    private ITransferAccountsZfbDetailService transferAccountsZfbDetailService;
    @Autowired
    private ITransferAccountsZfbBatchDAO transferAccountsZfbBatchDAO;
    @Autowired
    private ITransferAccountsPeopleService transferAccountsPeopleService;
    @Autowired
    private ITransferAccountsVxBatchService transferAccountsVxBatchService;
    @Autowired
    private ITransferAccountsVxDetailService transferAccountsVxDetailService;
    @Autowired
    private IEmpInfoService empInfoService;
    @Autowired
    private ITransferAccountInfoService transferAccountInfoService;
    @Autowired
    private ITransferAccountsZfbSafetyBatchService transferAccountsZfbSafetyBatchService;
    @Autowired
    private ITransferAccountsZfbSafetyDetailService transferAccountsZfbSafetyDetailService;
    @Autowired
    private ITransferAccountsBankBatchService transferAccountsBankBatchService;
    @Autowired
    private ITransferAccountsBankDetailService transferAccountsBankDetailService;

    @Autowired
    private IApproProviderService approProviderService;
    @Autowired
    private IUserProviderService userProviderService;
    @Autowired
    private ISiteConfigProviderService siteConfigProviderService;
    @Autowired
    private IMessageProviderService messageProviderService;
    @Autowired
    private ICache cache;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private IFileProviderService fileProviderService;

    @Autowired
    private HrOneKeyPayProperties hrOneKeyPayProperties;

    /**
     * 审批中
     */
    private static final int APPROVING = 1;

    /**
     * 审批驳回
     */
    private static final int APPROVED_REJECT = 2;

    /**
     * 审批通过
     */
    private static final int APPROVED_PASS = 3;

    /**
     * 抄送我
     */
    private static final int APPROVE_NOTIFIER_ME = 4;


    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public TransferAccountsBatchVO save(TransferAccountsBatchAddDTO transferAccountsBatchAddDTO, TokenUser tokenUser) {
        // 转DO
        TransferAccountsBatchDO transferAccountsBatchDO = transferAccountsBatchAddDTO.to(TransferAccountsBatchDO.class);
        UserDO userDO = userProviderService.selectUserById(tokenUser.getUserId());
        ParamException.notNull(userDO, "当前登录用户不存在或已删除，请重新登录！");
        transferAccountsBatchDO.setCreateUserName(userDO.getName());
        // 校验名称是否重复
        if (CollectionKit.isNotEmpty(selectByOrderTitle(transferAccountsBatchAddDTO.getOrderTitle(),
                tokenUser.getSiteId()))) {
            throw new ParamException("批次名称重复，请重新输入!");
        }
        // 保存
        transferAccountsBatchDO = save(transferAccountsBatchDO, tokenUser.getSiteId(), tokenUser.getUserId());
        return transferAccountsBatchDO.to(TransferAccountsBatchVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public TransferAccountsBatchVO saveLogic(TransferAccountsBatchAddDTO transferAccountsBatchAddDTO, TokenUser tokenUser) {
        // 校验名称是否重复
        if (CollectionKit.isNotEmpty(selectByOrderTitle(transferAccountsBatchAddDTO.getOrderTitle(),
                tokenUser.getSiteId()))) {
            throw new ParamException("批次名称重复，请重新输入!");
        }
        // 转DO
        TransferAccountsBatchDO transferAccountsBatchDO = transferAccountsBatchAddDTO.to(TransferAccountsBatchDO.class);
        // 计算总金额、总人数
        List<TransferAccountsDetailAddDTO> transferAccountsDetailAddDTOList = transferAccountsBatchAddDTO
                .getTransferAccountsDetailAddDTOList();
        BigDecimal moneyAll = new BigDecimal("0");
        int totalCount = 0;
        for (TransferAccountsDetailAddDTO transferAccountsDetailAddDTO : transferAccountsDetailAddDTOList) {
            totalCount++;
            BigDecimal money = transferAccountsDetailAddDTO.getMoney();
            if (money == null) {
                continue;
            }
            moneyAll = moneyAll.add(money);
        }
        transferAccountsBatchDO.setTotalCount(totalCount);
        transferAccountsBatchDO.setMoney(moneyAll);
        // 保存
        UserDO userDO = userProviderService.selectUserById(tokenUser.getUserId());
        ParamException.notNull(userDO, "当前登录用户不存在或已删除，请重新登录！");
        transferAccountsBatchDO.setCreateUserName(userDO.getName());
        transferAccountsBatchDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.WAIT_APPLICATION.getValue());
        transferAccountsBatchDO.setPayStatus(TransferAccountsPayStatusEnum.NO_PAY.getValue());
        transferAccountsBatchDO = save(transferAccountsBatchDO, tokenUser.getSiteId(), tokenUser.getUserId());
        // 保存明细信息
        for (TransferAccountsDetailAddDTO transferAccountsDetailAddDTO : transferAccountsBatchAddDTO.getTransferAccountsDetailAddDTOList()) {
            transferAccountsDetailAddDTO.setBatchId(transferAccountsBatchDO.getId());
            transferAccountsDetailAddDTO.setType(transferAccountsBatchDO.getType());
            transferAccountsDetailAddDTO.setSubjectStatus(TransferAccountsSubjectStatusEnum.WAIT_APPLICATION.getValue());
            transferAccountsDetailAddDTO.setPayStatus(TransferAccountsPayStatusEnum.NO_PAY.getValue());
            transferAccountsDetailAddDTO.setOrderTitle(transferAccountsBatchDO.getOrderTitle());
            transferAccountsDetailService.save(transferAccountsDetailAddDTO, tokenUser);
        }
        return transferAccountsBatchDO.to(TransferAccountsBatchVO.class);
    }

    @Override
    public TransferAccountsBatchVO saveAgainByBatchId(TransferAccountsBatchAgainAddDTO transferAccountsBatchAgainAddDTO, TokenUser tokenUser) {
        String batchId = transferAccountsBatchAgainAddDTO.getBatchId();
        // 查询批次信息
        TransferAccountsBatchDO transferAccountsBatchDO = this.selectDOById(batchId, tokenUser.getSiteId());
        ParamException.notNull(transferAccountsBatchDO, "重新发起的批次不存在或已删除，请刷新页面重试!");
        if (!TransferAccountsSubjectStatusEnum.APPROVE_REJECT.getValue().equals(transferAccountsBatchDO.getSubjectStatus())) {
            throw new ParamException("批次状态不是驳回不能重新发起！");
        }
        // 保存批次信息  构建AddDTO
        TransferAccountsBatchAddDTO transferAccountsBatchAddDTO = transferAccountsBatchDO.to(TransferAccountsBatchAddDTO.class);
        transferAccountsBatchAddDTO.setOrderTitle(transferAccountsBatchAgainAddDTO.getOrderTitle());
        if (StrKit.isNotEmpty(transferAccountsBatchAgainAddDTO.getRemark())) {
            transferAccountsBatchAddDTO.setRemark(transferAccountsBatchAgainAddDTO.getRemark());
        }
        transferAccountsBatchAddDTO.setUserApproId(null);
        List<TransferAccountsDetailAddDTO> transferAccountsDetailAddDTOList = new ArrayList<>();
        List<TransferAccountsDetailDO> transferAccountsDetailDOList = transferAccountsDetailService
                .selectByBatchId(batchId, tokenUser.getSiteId());
        for (TransferAccountsDetailDO transferAccountsDetailDO : transferAccountsDetailDOList) {
            transferAccountsDetailAddDTOList.add(transferAccountsDetailDO.to(TransferAccountsDetailAddDTO.class));
        }
        transferAccountsBatchAddDTO.setTransferAccountsDetailAddDTOList(transferAccountsDetailAddDTOList);
        return this.saveLogic(transferAccountsBatchAddDTO, tokenUser);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public TransferAccountsDetailImportAllVO saveBatchAndDetail(TransferBuildDetailQuery transferBuildDetailQuery, TokenUser tokenUser) {

        ParamException.notNull(transferBuildDetailQuery.getTransferAccountsBatchAddDTO(), "批次相关信息不能为空");
        TransferAccountsBatchAddDTO transferAccountsBatchAddDTO = transferBuildDetailQuery.getTransferAccountsBatchAddDTO();
        // 构造明细数据
        TransferAccountsDetailImportAllVO transferAccountsDetailImportAllVO =
                transferAccountsDetailService.buildDetailAddDTOList(transferBuildDetailQuery, tokenUser);
        // 一次的数据不能超过300
        if (transferAccountsDetailImportAllVO.getSuccessTransferAccountsDetailAddDTOList().size() > 500) {
            throw new ParamException("一个批次不能超过500人");
        }
        // 识别成功的数据 添加到批次add中
        transferAccountsBatchAddDTO.setTransferAccountsDetailAddDTOList(
                transferAccountsDetailImportAllVO.getSuccessTransferAccountsDetailAddDTOList());
        // 保存批次信息
        TransferAccountsBatchVO transferAccountsBatchVO = this.saveLogic(transferAccountsBatchAddDTO, tokenUser);
        // 保存的批次信息添加到返回数据中
        transferAccountsDetailImportAllVO.setTransferAccountsBatchVO(transferAccountsBatchVO);
        return transferAccountsDetailImportAllVO;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(TransferAccountsBatchUpdateDTO transferAccountsBatchUpdateDTO, TokenUser tokenUser) {
        //转DO
        TransferAccountsBatchDO transferAccountsBatchDO = transferAccountsBatchUpdateDTO.to(TransferAccountsBatchDO.class);
        //根据主键更新，只更新非null值
        return updateByIdSelective(transferAccountsBatchDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, String... transferAccountsBatchIds) {
        for (String transferAccountsBatchId : transferAccountsBatchIds) {
            // 批量删除批次明细
            transferAccountsDetailService.deleteByBatchId(transferAccountsBatchId, tokenUser);
        }
        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) transferAccountsBatchIds);
    }


    @Override
    public TransferAccountsBatchVO selectById(String transferAccountsBatchId, String siteId) {
        TransferAccountsBatchDO transferAccountsBatchDO = selectDOById(transferAccountsBatchId, siteId);
        if (transferAccountsBatchDO == null) {
            return null;
        }
        TransferAccountsBatchVO transferAccountsBatchVO = transferAccountsBatchDO.to(TransferAccountsBatchVO.class);
        setBatchOtherInfo(transferAccountsBatchVO, siteId);
        return transferAccountsBatchVO;
    }

    // 设置VO需要展示的其他信息
    private void setBatchOtherInfo(TransferAccountsBatchVO transferAccountsBatchVO, String siteId) {
        // 查询明细
        List<TransferAccountsDetailDO> transferAccountsDetailDOList = transferAccountsDetailService
                .selectByBatchId(transferAccountsBatchVO.getId(), siteId);
        if (CollectionKit.isNotEmpty(transferAccountsDetailDOList)) {
            int errorNumber = 0;
            BigDecimal errorMoney = new BigDecimal("0");
            BigDecimal feeAll = new BigDecimal("0");
            for (TransferAccountsDetailDO transferAccountsDetailDO : transferAccountsDetailDOList) {
                // 查询全部的手续费
                if (transferAccountsDetailDO.getFee() != null) {
                    feeAll = feeAll.add(transferAccountsDetailDO.getFee());
                }
                if (transferAccountsDetailDO.getIsDispose() == null || transferAccountsDetailDO.getIsDispose()) {
                    continue;
                }
                if (transferAccountsDetailDO.getMoney() == null) {
                    continue;
                }
                errorNumber++;
                errorMoney = errorMoney.add(transferAccountsDetailDO.getMoney());
            }
            transferAccountsBatchVO.setFeeAll(feeAll);
            transferAccountsBatchVO.setErrorNumber(errorNumber);
            transferAccountsBatchVO.setErrorMoney(errorMoney);
        }
        // 如果是支付宝(非安全发)的支付中 查询payUrl
        if (TransferAccountsTypeEnum.ZFB.getValue().equals(transferAccountsBatchVO.getType())
                && TransferAccountsSubjectStatusEnum.WAIT_ISSUE.getValue().equals(transferAccountsBatchVO.getSubjectStatus())
                && TransferAccountsPayStatusEnum.PAY_ING.getValue().equals(transferAccountsBatchVO.getPayStatus())
                && !TransferAccountsSecondTypeEnum.ZFB_SAFETY_PAY.getValue().equals(transferAccountsBatchVO.getSecondType())) {
            TransferAccountsZfbBatchVO transferAccountsZfbBatchVO = transferAccountsZfbBatchService
                    .selectById(transferAccountsBatchVO.getThirdpartyBatchId(), siteId);
            ParamException.notNull(transferAccountsZfbBatchVO, "没有找到第三方的批次信息，请联系管理员或刷新重试!");
            transferAccountsBatchVO.setPayUrl(transferAccountsZfbBatchVO.getPayUrl());
            transferAccountsBatchVO.setPayUrlMove(transferAccountsZfbBatchVO.getPayUrlTwo());
            transferAccountsBatchVO.setUrlMoveQR(transferAccountsZfbBatchVO.getPayUrlTwoImage());
        }
    }

    @Override
    public List<TransferAccountsBatchListVO> select(TransferAccountsBatchQuery transferAccountsBatchQuery, String siteId) {
        List<TransferAccountsBatchDO> transferAccountsBatchDOList = selectDO(transferAccountsBatchQuery, siteId);
        if (CollectionUtils.isEmpty(transferAccountsBatchDOList)) {
            return transferAccountsBatchDOList == null ? null : new ArrayList<>();
        }
        return transferAccountsBatchDOList.stream()
                .map(transferAccountsBatchDO -> transferAccountsBatchDO.to(TransferAccountsBatchListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(TransferAccountsBatchQuery transferAccountsBatchQuery, String siteId) {
        Example example = buildExample(transferAccountsBatchQuery, siteId);
        return transferAccountsBatchDAO.selectCountByExample(example);
    }

    @Override
    public Page<TransferAccountsBatchListVO> selectPage(TransferAccountsBatchQuery transferAccountsBatchQuery, String siteId) {
        if (StrKit.isNotEmpty(transferAccountsBatchQuery.getNotSubjectStatusListStr())) {
            List<String> notSubjectStatusList = ArrayKit
                    .split(transferAccountsBatchQuery.getNotSubjectStatusListStr(), ",");
            transferAccountsBatchQuery.setNotSubjectStatusList(notSubjectStatusList);
        }
        Example example = buildExample(transferAccountsBatchQuery, siteId);
        Page<TransferAccountsBatchDO> page = transferAccountsBatchDAO.selectPageByExample(example,
                transferAccountsBatchQuery.getPageNo(),
                transferAccountsBatchQuery.getPageSize());

        return page.to(TransferAccountsBatchListVO.class);
    }

    @Override
    public TransferAccountsBatchDO selectDOById(String transferAccountsBatchId, String siteId) {
        return listById(transferAccountsBatchId, siteId);
    }

    @Override
    public List<TransferAccountsBatchDO> selectDO(TransferAccountsBatchQuery transferAccountsBatchQuery, String siteId) {
        Example example = buildExample(transferAccountsBatchQuery, siteId);
        return transferAccountsBatchDAO.selectByExample(example);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int updateDetailSubjectStatusByBatchId(String transferAccountsBatchId, Integer subjectStatus, String siteId) {
        List<TransferAccountsDetailDO> transferAccountsDetailDOList = transferAccountsDetailService
                .selectByBatchId(transferAccountsBatchId, siteId);
        if (CollectionUtils.isEmpty(transferAccountsDetailDOList)) {
            throw new ParamException("批次对应的明细为空，无法修改状态!");
        }
        for (TransferAccountsDetailDO transferAccountsDetailDO : transferAccountsDetailDOList) {
            if (transferAccountsDetailDO.getMoney() == null) {
                throw new ParamException(transferAccountsDetailDO.getName() + "的明细金额不能为空");
            }
            if (StrKit.isEmpty(transferAccountsDetailDO.getIdentity())) {
                throw new ParamException(transferAccountsDetailDO.getName() + "的账号不能为空");
            }
            transferAccountsDetailDO.setSubjectStatus(subjectStatus);
            transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO);
        }
        return transferAccountsDetailDOList.size();
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public UserApproVO launchApproveByCompanyId(String transferAccountsBatchId, String flowId, TokenUser tokenUser) {
        TransferAccountsBatchDO accountsBatchDO = selectDOById(transferAccountsBatchId, tokenUser.getSiteId());
        ParamException.notNull(accountsBatchDO, "转账批次不存在");
        // 用工单位id设置为和前端约定的固定字符串
        String companyId = HrConstants.COMPANY_APPROVE_ONE_KEY_PAY;
        // 校验转账批次审批状态
        if (!TransferAccountsSubjectStatusEnum.WAIT_APPLICATION.getValue().equals(accountsBatchDO.getSubjectStatus())) {
            throw new ParamException("账单不是待申请状态，无法发起审批");
        }
        NewApproDTO newApproDTO = new NewApproDTO();
        // 这里可以放置一些自定义的数据，前端在绘制审批明细时使用。这里放置是转账批次对象
        newApproDTO.setProps(JsonKit.toJSONString(accountsBatchDO));
        // 审批的标题
        newApproDTO.setTitle(accountsBatchDO.getOrderTitle());
        // 审批关联的业务类型，约定转账批次的subjectType是transferAccounts
        newApproDTO.setSubjectType(HrConstants.SUBJECT_TYPE_ONE_KEY_PAY);
        // 审批关联的业务id，转账批次id，可以利用这个到时可以根据转账批次id查询该转账批次的所有审批历史
        newApproDTO.setSubjectId(accountsBatchDO.getId());
        // 设置审批的流程id，transferAccounts是超级后台设置的一键发薪审批类型的code
        if (!approProviderService.checkFlowId(HrConstants.APPROVE_ONE_KEY_PAY, companyId, flowId, tokenUser)) {
            log.error("校验审批模板错误：type：{}，companyId：{}，flowId：{}", HrConstants.APPROVE_ONE_KEY_PAY, companyId, flowId);
            throw new ParamException("审批流程错误,请检查用工单位是否有指定类型的审批流程");
        }
        newApproDTO.setFlowId(flowId);
        // accountsBatchDO的所有属性会匹配审批表单里的字段，匹配到了就会设置在审批表单里
        newApproDTO.setPojo(accountsBatchDO);
        // 发起审批
        UserApproVO userApproVO = approProviderService.addUserAppro(newApproDTO, tokenUser);
        ParamException.notNull(userApproVO, "发起审批失败，请联系管理员");
        // 修改转账批次审批状态
        updateApproveStatus(transferAccountsBatchId, userApproVO.getId(), tokenUser);
        return userApproVO;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public UserApproVO startApprover(String transferAccountsBatchId, String flowId, TokenUser tokenUser) {
        // 一键发薪缓存key
        String key = CacheKeyKit.getOneKeyPayApproKey(transferAccountsBatchId, flowId, tokenUser.getSiteId());
        // 根据批次id加锁
        RLock lock = redissonClient.getLock(key);
        boolean hasLock;
        try {
            // 加锁，等待0秒，上锁以后300秒自动解锁
            hasLock = lock.tryLock(0, 300, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if (!hasLock) {
            log.warn("重复发起发薪审批，id{},flowId:{}，userId:{}", transferAccountsBatchId, flowId, tokenUser.getUserId());
            throw new ParamException("一个批次只能发起一次申请，请勿重复提交");
        }

        try {
            // 先修改批次明细的业务状态为审批中
            updateDetailSubjectStatusByBatchId(transferAccountsBatchId,
                    TransferAccountsSubjectStatusEnum.APPROVE_ING.getValue(), tokenUser.getSiteId());
            // 发起审批
            return launchApproveByCompanyId(transferAccountsBatchId, flowId, tokenUser);
        } finally {
            //释放锁
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    public Page<TransferAccountsBatchListVO> pageApproving(TransferAccountsBatchQuery transferAccountsBatchQuery,
                                                           TokenUser tokenUser) {
        // 是否有符合筛选条件的转账批次
        List<TransferAccountsBatchListVO> batchListVOS = select(transferAccountsBatchQuery, tokenUser.getSiteId());
        // 查询待我审批当前人的审批
        UserFlowApproQuery userFlowApproQuery = new UserFlowApproQuery();
        // 用工单位id设置为和前端约定的固定字符串
        userFlowApproQuery.setCompanyId(HrConstants.COMPANY_APPROVE_ONE_KEY_PAY);
        List<UserApproNodeInfoVO> userApproNodeInfoVOS = approProviderService
                .listUserApprovesWaitApproveByMe(userFlowApproQuery, tokenUser);
        return pageParseData(batchListVOS, userApproNodeInfoVOS, transferAccountsBatchQuery.getPageNo(),
                transferAccountsBatchQuery.getPageSize());
    }

    @Override
    public Page<TransferAccountsBatchListVO> pageApprovedPass(TransferAccountsBatchQuery transferAccountsBatchQuery,
                                                              TokenUser tokenUser) {
        // 是否有符合筛选条件的转账批次
        List<TransferAccountsBatchListVO> batchListVOS = select(transferAccountsBatchQuery, tokenUser.getSiteId());
        // 查询已通过的审批
        UserFlowApproQuery userFlowApproQuery = new UserFlowApproQuery();
        // 用工单位id设置为和前端约定的固定字符串
        userFlowApproQuery.setCompanyId(HrConstants.COMPANY_APPROVE_ONE_KEY_PAY);
        List<UserApproNodeInfoVO> userApproNodeInfoVOS = approProviderService
                .listPassedApproves(userFlowApproQuery, tokenUser);
        return pageParseData(batchListVOS, userApproNodeInfoVOS, transferAccountsBatchQuery.getPageNo(),
                transferAccountsBatchQuery.getPageSize());
    }


    @Override
    public Page<TransferAccountsBatchListVO> pageApprovedReject(TransferAccountsBatchQuery transferAccountsBatchQuery,
                                                                TokenUser tokenUser) {
        // 是否有符合筛选条件的转账批次
        List<TransferAccountsBatchListVO> batchListVOS = select(transferAccountsBatchQuery, tokenUser.getSiteId());
        // 查询待我审批当前人的审批
        UserFlowApproQuery userFlowApproQuery = new UserFlowApproQuery();
        // 用工单位id设置为和前端约定的固定字符串
        userFlowApproQuery.setCompanyId(HrConstants.COMPANY_APPROVE_ONE_KEY_PAY);
        List<UserApproNodeInfoVO> userApproNodeInfoVOS = approProviderService
                .listRejectedApproves(userFlowApproQuery, tokenUser);
        return pageParseData(batchListVOS, userApproNodeInfoVOS, transferAccountsBatchQuery.getPageNo(),
                transferAccountsBatchQuery.getPageSize());
    }

    @Override
    public Page<TransferAccountsBatchListVO> pageApproveNotifierMe(TransferAccountsBatchQuery transferAccountsBatchQuery,
                                                                   TokenUser tokenUser) {
        // 是否有符合筛选条件的转账批次
        List<TransferAccountsBatchListVO> batchListVOS = select(transferAccountsBatchQuery, tokenUser.getSiteId());
        // 查询抄送当前人的审批
        UserFlowApproQuery userFlowApproQuery = new UserFlowApproQuery();
        // 用工单位id设置为和前端约定的固定字符串
        userFlowApproQuery.setCompanyId(HrConstants.COMPANY_APPROVE_ONE_KEY_PAY);
        List<UserApproNodeInfoVO> userApproNodeInfoVOS = approProviderService
                .listUserApprosNotifierMe(userFlowApproQuery, tokenUser);
        return pageParseData(batchListVOS, userApproNodeInfoVOS, transferAccountsBatchQuery.getPageNo(),
                transferAccountsBatchQuery.getPageSize());
    }

    /**
     * 取审批列表和转账批次列表的交集并分页
     *
     * @param batchList             转账批次列表
     * @param userApproNodeInfoList 审批列表
     * @param pageNo                页码
     * @param pageSize              每页多少条数据
     * @return page
     */
    private Page<TransferAccountsBatchListVO> pageParseData(List<TransferAccountsBatchListVO> batchList,
                                                            List<UserApproNodeInfoVO> userApproNodeInfoList,
                                                            Integer pageNo, Integer pageSize) {
        if (CollectionUtils.isEmpty(batchList)) {
            return new Page<>(pageSize, pageNo);
        }
        // 只保留一键发薪类型的审批
        userApproNodeInfoList.removeIf(s -> !HrConstants.APPROVE_ONE_KEY_PAY.equals(s.getApproTypeCode()));
        // 是否有审批记录
        if (CollectionUtils.isEmpty(userApproNodeInfoList)) {
            return new Page<>(pageSize, pageNo);
        }
        // 人员申请审批id-审批节点 Map
        Map<String, UserApproNodeInfoVO> userApproIdNodeInfoMap = userApproNodeInfoList.stream()
                // 当前人可能存于与多个节点，是同一个审批记录，保留第一条记录
                .collect(Collectors.toMap(UserApproNodeInfoVO::getUserApproId, v -> v, (v1, v2) -> v1));
        // 去除审批id为空的且不在审批列表里的
        batchList = batchList.stream()
                .filter(s -> StrKit.isNotEmpty(s.getUserApproId())
                        && userApproIdNodeInfoMap.containsKey(s.getUserApproId()))
                .peek(s -> {
                    UserApproNodeInfoVO userApproNodeInfoVO = userApproIdNodeInfoMap.get(s.getUserApproId());
                    s.setUserApproverId(userApproNodeInfoVO.getUserApproverId());
                }).collect(Collectors.toList());
        return PageKit.startPage(batchList, pageNo, pageSize);
    }

    /**
     * 取审批列表和转账批次列表的交集
     *
     * @param batchList             转账批次列表
     * @param userApproNodeInfoList 审批列表
     * @param approvalStatus        审批状态
     * @return page
     */
    private List<TransferAccountsBatchListVO> listParseData(List<TransferAccountsBatchListVO> batchList,
                                                            List<UserApproNodeInfoVO> userApproNodeInfoList,
                                                            Integer approvalStatus) {
        if (CollectionUtils.isEmpty(batchList)) {
            return new ArrayList<>();
        }
        // 只保留一键发薪类型的审批
        userApproNodeInfoList.removeIf(s -> !HrConstants.APPROVE_ONE_KEY_PAY.equals(s.getApproTypeCode()));
        // 是否有审批记录
        if (CollectionUtils.isEmpty(userApproNodeInfoList)) {
            return new ArrayList<>();
        }
        // 人员申请审批id-审批节点 Map
        Map<String, UserApproNodeInfoVO> userApproIdNodeInfoMap = userApproNodeInfoList.stream()
                .collect(Collectors.toMap(UserApproNodeInfoVO::getUserApproId, v -> v));
        // 去除审批id为空的且不在审批列表里的
        batchList = batchList.stream()
                .filter(s -> StrKit.isNotEmpty(s.getUserApproId())
                        && userApproIdNodeInfoMap.containsKey(s.getUserApproId()))
                .peek(s -> {
                    UserApproNodeInfoVO userApproNodeInfoVO = userApproIdNodeInfoMap.get(s.getUserApproId());
                    s.setUserApproverId(userApproNodeInfoVO.getUserApproverId());
                    s.setApproveStatus(approvalStatus);
                }).collect(Collectors.toList());
        return batchList;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public TransferAccountsDetailImportAllVO saveBatchAndDetailByFile(File file, String companyId, String companyName,
                                                                      Integer type, String orderTitle,
                                                                      String remark, TokenUser tokenUser) {
        // 识别信息
        TransferAccountsDetailImportAllVO transferAccountsDetailImportAllVO = transferAccountsDetailService
                .recognitionDetailInfo(file, null, companyId, type, tokenUser);
        // 创建批次
        TransferAccountsBatchAddDTO transferAccountsBatchAddDTO = new TransferAccountsBatchAddDTO();
        transferAccountsBatchAddDTO.setCompanyId(companyId);
        transferAccountsBatchAddDTO.setCompanyName(companyName);
        transferAccountsBatchAddDTO.setType(type);
        transferAccountsBatchAddDTO.setOrderTitle(orderTitle);
        transferAccountsBatchAddDTO.setRemark(remark);
        // 一次的数据不能超过500
        if (transferAccountsDetailImportAllVO.getSuccessTransferAccountsDetailAddDTOList().size() > 500) {
            throw new ParamException("一个批次不能超过500人");
        }
        // 添加成功的信息该批次明细中
        transferAccountsBatchAddDTO.setTransferAccountsDetailAddDTOList(
                transferAccountsDetailImportAllVO.getSuccessTransferAccountsDetailAddDTOList());
        TransferAccountsBatchVO transferAccountsBatchVO = this.saveLogic(transferAccountsBatchAddDTO, tokenUser);
        // 把保存成功的批次信息设置到返回值中
        transferAccountsDetailImportAllVO.setTransferAccountsBatchVO(transferAccountsBatchVO);
        return transferAccountsDetailImportAllVO;
    }

    // 根据批次id查询批次DO 会校验该批次是否可以发放
    private TransferAccountsBatchDO selectStartBatch(String batchId, TokenUser tokenUser) {
        TransferAccountsBatchDO transferAccountsBatchDO = this.selectDOById(batchId, tokenUser.getSiteId());
        ParamException.notNull(transferAccountsBatchDO, "批次id不正确!");
        if (StrKit.isEmpty(transferAccountsBatchDO.getOrderTitle())) {
            throw new ParamException("批次的转账标题不能为空");
        }
        if (transferAccountsBatchDO.getMoney() == null) {
            throw new ParamException("转账总金额不能为空");
        }
        if (transferAccountsBatchDO.getTotalCount() == null) {
            throw new ParamException("转账总笔数不能为空");
        }
        // 校验批次状态  待发放  和 发放失败的可以发起创建
        if (TransferAccountsSubjectStatusEnum.WAIT_ISSUE.getValue().equals(transferAccountsBatchDO.getSubjectStatus())
                && TransferAccountsPayStatusEnum.NO_PAY.getValue().equals(transferAccountsBatchDO.getPayStatus())) {
            return transferAccountsBatchDO;
        }
        if (TransferAccountsSubjectStatusEnum.YET_ISSUE.getValue().equals(transferAccountsBatchDO.getSubjectStatus())
                && TransferAccountsPayStatusEnum.FAIL_PAY.getValue().equals(transferAccountsBatchDO.getPayStatus())) {
            return transferAccountsBatchDO;
        }
        throw new ParamException("该批次的状态非法，无法创建支付订单!");
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public AliPayUrlVO zfbStartTransferAccounts(TransferStartZfbBatchQuery transferStartZfbBatchQuery, TokenUser tokenUser) {
        String batchId = transferStartZfbBatchQuery.getBatchId();
        String appAuthToken = transferStartZfbBatchQuery.getAppAuthToken();
        String pid = transferStartZfbBatchQuery.getPid();
        // 根据批次id加锁
        RLock lock = redissonClient.getLock(batchId);
        boolean hasLock;
        try {
            // 加锁，等待0秒，上锁以后300秒自动解锁
            hasLock = lock.tryLock(0, 300, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if (!hasLock) {
            log.warn("支付宝创建批次的hr的批次id{},userId:{}", batchId, tokenUser.getUserId());
            throw new ParamException("一个批次只能提交一次，请勿重复提交");
        }
        try {

            TransferAccountsBatchDO transferAccountsBatchDO = this.selectStartBatch(batchId, tokenUser);
            if (StrKit.isEmpty(appAuthToken)) {
                throw new ParamException("appAuthToken不能为空");
            }
            LogKit.log("批次名为：" + transferAccountsBatchDO.getOrderTitle() + "，薪资发放",
                    null, "oneKeyPay", batchId, tokenUser);
            // 校验appAuthToken
            CertAlipayRequest certAlipayRequest = this.buildCertAlipayRequest();
            if (!Alipay.selectAppAuthTokenStatus(appAuthToken, certAlipayRequest)) {
                throw new ParamException("商户授权token失效，无法查询的订单信息，请引导商户前往授权后重试!");
            }
            // 根据批次id查询批次明细
            List<TransferAccountsDetailDO> transferAccountsDetailDOList = transferAccountsDetailService
                    .selectByBatchId(batchId, tokenUser.getSiteId());
            if (CollectionUtils.isEmpty(transferAccountsDetailDOList)) {
                throw new ParamException("根据批次id没有查询到批次的明细，创建批次订单失败！");
            }
            // 发起支付宝创建订单请求
            BatchOrderVO batchOrderVO = buildBatchOrderVO(transferAccountsBatchDO, transferAccountsDetailDOList,
                    pid, tokenUser);
            return launchTransferAccountsBatch(batchId, batchOrderVO.getOutBatchNo(), appAuthToken,
                    certAlipayRequest, batchOrderVO, tokenUser);
        } finally {
            //释放锁
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    // 保存支付宝相关的批次信息和明细，返回创建支付宝批次需要的JSON对象
    private BatchOrderVO buildBatchOrderVO(TransferAccountsBatchDO transferAccountsBatchDO,
                                           List<TransferAccountsDetailDO> transferAccountsDetailDOList,
                                           String pid, TokenUser tokenUser) {
        // 构造支付宝创建订单的JSON对象（批次信息）
        BatchOrderVO batchOrderVO = new BatchOrderVO();
        TransferAccountsZfbBatchVO transferAccountsZfbBatchVO = saveZfbBatch(tokenUser, transferAccountsBatchDO,
                pid, batchOrderVO);

        List<TransOrderVO> transOrderListVOList = new ArrayList<>();
        // 创建支付宝批次明细信息
        for (TransferAccountsDetailDO transferAccountsDetailDO : transferAccountsDetailDOList) {
            saveZfbDetail(transferAccountsZfbBatchVO, transferAccountsDetailDO,
                    tokenUser, transOrderListVOList);
        }
        batchOrderVO.setTransOrderList(transOrderListVOList);
        return batchOrderVO;
    }

    /**
     * 根据hr批次信息创建支付宝批次信息
     *
     * @param tokenUser               当前用户
     * @param transferAccountsBatchDO hr批次信息
     * @param pid                     pid
     * @param batchOrderVO            支付宝批次下单需要的JSON对象
     * @return 支付宝批次信息
     */
    private TransferAccountsZfbBatchVO saveZfbBatch(TokenUser tokenUser,
                                                    TransferAccountsBatchDO transferAccountsBatchDO,
                                                    String pid, BatchOrderVO batchOrderVO) {
        UserDO userDO = userProviderService.selectUserById(tokenUser.getUserId());
        ParamException.notNull(userDO, "当前用户不存在或已删除，请重新登录！");
        // 创建支付宝批次表信息
        TransferAccountsZfbBatchAddDTO transferAccountsZfbBatchAddDTO = new TransferAccountsZfbBatchAddDTO();
        transferAccountsZfbBatchAddDTO.setBatchId(transferAccountsBatchDO.getId());
        transferAccountsZfbBatchAddDTO.setOrderTitle(transferAccountsBatchDO.getOrderTitle());
        transferAccountsZfbBatchAddDTO.setRemark(transferAccountsBatchDO.getRemark());
        transferAccountsZfbBatchAddDTO.setPayerUserId(pid);
        transferAccountsZfbBatchAddDTO.setTotalCount(transferAccountsBatchDO.getTotalCount() + "");
        transferAccountsZfbBatchAddDTO.setMoney(transferAccountsBatchDO.getMoney().toString());
        transferAccountsZfbBatchAddDTO.setPayerId(pid);
        TransferAccountsZfbBatchVO transferAccountsZfbBatchVO = transferAccountsZfbBatchService
                .save(transferAccountsZfbBatchAddDTO, tokenUser);
        // 设置批次的相关信息
        transferAccountsBatchDO.setThirdpartyBatchId(transferAccountsZfbBatchVO.getId());
        transferAccountsBatchDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.WAIT_ISSUE.getValue());
        transferAccountsBatchDO.setPayStatus(TransferAccountsPayStatusEnum.PAY_ING.getValue());
        transferAccountsBatchDO.setSecondType(TransferAccountsSecondTypeEnum.ZFB_BALANCE.getValue());
        transferAccountsBatchDO.setIssueUserId(tokenUser.getUserId());
        transferAccountsBatchDO.setIssueUserName(userDO.getName());
        transferAccountsBatchDO.setIssueCreate(new Date());
        // 根据pid 查询账户信息
        TransferAccountInfoDO transferAccountInfoDO = transferAccountInfoService
                .selectBySiteIdAndAccount(TransferAccountsTypeEnum.ZFB.getValue(), pid, tokenUser.getSiteId());
        String accountName = transferAccountInfoDO == null ? pid : transferAccountInfoDO.getName();
        transferAccountsBatchDO.setIssueAccountName(accountName);
        // 修改批次信息
        this.updateByIdSelective(transferAccountsBatchDO, tokenUser.getSiteId(), tokenUser.getUserId());

        // 使用第三方表的id 作为此次在支付宝创建的批次id
        batchOrderVO.setOutBatchNo(transferAccountsZfbBatchVO.getId());
        batchOrderVO.setProductCode("BATCH_API_TO_ACC");
        batchOrderVO.setBizScene("MESSAGE_BATCH_PAY");
        batchOrderVO.setOrderTitle(transferAccountsBatchDO.getOrderTitle());
        batchOrderVO.setTotalTransAmount(transferAccountsBatchDO.getMoney().doubleValue());
        batchOrderVO.setTotalCount(transferAccountsBatchDO.getTotalCount() + "");
        Date timeExpire = transferAccountsBatchDO.getTimeExpire();
        if (timeExpire != null) {
            if (timeExpire.getTime() < System.currentTimeMillis()) {
                throw new ParamException("支付到期时间不能小于现在的时间!");
            }
            batchOrderVO.setTimeExpire(DateKit8.format(timeExpire, DateKit8.DATE_TIME_WITHOUT_SECONDS));
        }
        batchOrderVO.setRemark(transferAccountsBatchDO.getRemark());
        return transferAccountsZfbBatchVO;
    }

    /**
     * 根据hr的批次明细信息创建支付宝的批次明细信息
     *
     * @param transferAccountsZfbBatchVO 支付宝的批次信息
     * @param transferAccountsDetailDO   hr的批次明细信息
     * @param tokenUser                  当前用户
     * @param transOrderListVOList       支付宝批次下单需要的JSON对象List
     */
    private void saveZfbDetail(TransferAccountsZfbBatchVO transferAccountsZfbBatchVO,
                               TransferAccountsDetailDO transferAccountsDetailDO,
                               TokenUser tokenUser,
                               List<TransOrderVO> transOrderListVOList) {
        TransferAccountsZfbDetailAddDTO transferAccountsZfbDetailAddDTO = new TransferAccountsZfbDetailAddDTO();
        transferAccountsZfbDetailAddDTO.setZfbBatchId(transferAccountsZfbBatchVO.getId());
        transferAccountsZfbDetailAddDTO.setName(transferAccountsDetailDO.getName());
        transferAccountsZfbDetailAddDTO.setPayeeAccount(transferAccountsDetailDO.getIdentity());
        transferAccountsZfbDetailAddDTO.setMoney(transferAccountsDetailDO.getMoney());
        transferAccountsZfbDetailAddDTO.setOrderTitle(transferAccountsDetailDO.getOrderTitle());
        transferAccountsZfbDetailAddDTO.setRemark(transferAccountsDetailDO.getRemark());
        TransferAccountsZfbDetailVO transferAccountsZfbDetailVO = transferAccountsZfbDetailService
                .save(transferAccountsZfbDetailAddDTO, tokenUser);
        // 修改明细表信息
        transferAccountsDetailDO.setThirdpartyDetailId(transferAccountsZfbDetailVO.getId());
        transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO, tokenUser.getSiteId(),
                tokenUser.getUserId());
        // 构建收款明细信息JSON
        TransOrderVO transOrder = new TransOrderVO();
        transOrder.setOutBizNo(transferAccountsZfbDetailVO.getId());
        if (transferAccountsDetailDO.getMoney() == null) {
            throw new ParamException("单个收款信息金额不能为空，请检查是否有没有填写的收款金额！");
        }
        transOrder.setTransAmount(transferAccountsDetailDO.getMoney().toString());
        transOrder.setOrderTitle(transferAccountsDetailDO.getOrderTitle());
        transOrder.setRemark(transferAccountsDetailDO.getRemark());
        PayeeInfoVO payeeInfoC = new PayeeInfoVO();
        if (StrKit.isEmpty(transferAccountsDetailDO.getIdentity())) {
            throw new ParamException("收款账号不能为空，请检查是否有没有填写的收款账号(登录支付宝的账号名：邮箱或手机号格式)");
        }
        payeeInfoC.setIdentity(transferAccountsDetailDO.getIdentity());
        payeeInfoC.setIdentityType("ALIPAY_LOGON_ID");
        if (StrKit.isEmpty(transferAccountsDetailDO.getName())) {
            throw new ParamException("收款真实姓名不能为空，请检查是否有没有填写");
        }
        payeeInfoC.setName(transferAccountsDetailDO.getName());
        transOrder.setPayeeInfo(payeeInfoC);
        transOrderListVOList.add(transOrder);
    }


    /**
     * 根据mode 创建该方式的支付URL
     *
     * @param transferAccountsBatchId    hr批次id
     * @param transferAccountsZfbBatchId 支付宝批次id
     * @param appAuthToken               商户授权token
     * @param certAlipayRequest          服务商相关信息
     * @param batchOrderVO               支付宝
     * @param tokenUser                  当前用户
     * @return URL 支付链接
     */
    private AliPayUrlVO launchTransferAccountsBatch(String transferAccountsBatchId, String transferAccountsZfbBatchId,
                                                    String appAuthToken, CertAlipayRequest certAlipayRequest,
                                                    BatchOrderVO batchOrderVO, TokenUser tokenUser) {
        TransferAccountsBatchDO transferAccountsBatchDO = new TransferAccountsBatchDO();
        // 查询回调地址
        String backUrl = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.BANK_URL, null);
        if (StrKit.isEmpty(backUrl)) {
            throw new ParamException("没有配置 批量转账使用的支付宝支付完成后的回调地址!");
        }
        // 创建批量付款单据
        String batchTransId = Alipay.alipayFundBatchCreateRequest(batchOrderVO, appAuthToken, certAlipayRequest);
        ParamException.notNull(batchTransId, "创建批量付款单据错误，请联系管理员查看原因！");
        // 支付批量转账单据
        String urlPc = Alipay.alipayFundTransPagePay(batchTransId, backUrl, appAuthToken, certAlipayRequest);
        String urlMove = Alipay.alipayMovePay(batchTransId, appAuthToken, certAlipayRequest);
        // 构造图片二维码，保存链接返回给前端
        String urlTwoImage = "";
        try {
            // 创建需要导出的文件
            String tempFilePath = FileKit.getSystemTempPath();
            String fileName = "whahy" + System.currentTimeMillis() + ".png";
            new File(tempFilePath).mkdir();
            tempFilePath += fileName;
            File file = new File(tempFilePath);
            OutputStream stream = new FileOutputStream(file);
            //获取一个二维码图片
            BitMatrix bitMatrix = ImageUtil.createCode(urlMove);
            MatrixToImageWriter.writeToStream(bitMatrix, "png", stream);
            FileInputStream input = new FileInputStream(file);
            MultipartFile multipartFile = new MockMultipartFile("copy" + file.getName(), file.getName(),
                    ContentType.APPLICATION_OCTET_STREAM.toString(), input);
            List<String> urlList = fileProviderService.generalUpload(multipartFile, "inline");
            if (CollectionKit.isNotEmpty(urlList)) {
                urlTwoImage = urlList.get(0);
            }
        } catch (IOException e) {
            log.error("生成移动支付二维码失败，失败原因：{}", e.getMessage());
        }
        // 保存支付宝返回的批次id 和 支付url 到支付宝的批次信息里
        TransferAccountsZfbBatchDO transferAccountsZfbBatchDO = new TransferAccountsZfbBatchDO();
        transferAccountsZfbBatchDO.setId(transferAccountsZfbBatchId);
        transferAccountsZfbBatchDO.setBatchTransId(batchTransId);
        transferAccountsZfbBatchDO.setPayUrl(urlPc);
        transferAccountsZfbBatchDO.setPayUrlTwo(urlMove);
        transferAccountsZfbBatchDO.setPayUrlTwoImage(urlTwoImage);
        transferAccountsZfbBatchService.updateByIdSelective(transferAccountsZfbBatchDO,
                tokenUser.getSiteId(), tokenUser.getUserId());

        transferAccountsBatchDO.setId(transferAccountsBatchId);
        transferAccountsBatchDO.setThirdPartyInteriorBatchId(batchTransId);
        this.updateByIdSelective(transferAccountsBatchDO);
        AliPayUrlVO aliPayUrlVO = new AliPayUrlVO();
        aliPayUrlVO.setUrlPc(urlPc);
        aliPayUrlVO.setUrlMove(urlMove);
        aliPayUrlVO.setUrlMoveQR(urlTwoImage);
        return aliPayUrlVO;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public boolean sendSafetyPayPhoneMessage(String phone, TokenUser tokenUser) {
        // 发送短信验证码
        ShortMessageDTO shortMessageDTO = new ShortMessageDTO();
        shortMessageDTO.setPhone(phone);
        shortMessageDTO.setScene("zfb_safety");
        return messageProviderService.sendShortMessage(shortMessageDTO, tokenUser);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public String zfbSafetyStartTransferAccounts(TransferStartZfbSafetyBatchQuery transferStartZfbSafetyBatchQuery, TokenUser tokenUser) {
        String accountId = transferStartZfbSafetyBatchQuery.getAccountId();
        String code = transferStartZfbSafetyBatchQuery.getCode();
        String batchId = transferStartZfbSafetyBatchQuery.getBatchId();
        // 查询账户
        TransferAccountInfoVO transferAccountInfoVO = transferAccountInfoService.selectById(accountId, tokenUser.getSiteId());
        ParamException.notNull(transferAccountInfoVO, "没有找到记账本账户，请刷新重试!");
        //校验短信验证码
        boolean check = messageProviderService.verifyCaptcha("zfb_safety", transferAccountInfoVO.getPhone(), code);
        if (!check) {
            throw new ParamException("验证码错误");
        }
        // 根据批次id加锁
        RLock lock = redissonClient.getLock(batchId);
        boolean hasLock;
        try {
            // 加锁，等待0秒，上锁以后1000秒自动解锁
            hasLock = lock.tryLock(0, 1000, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if (!hasLock) {
            log.warn("安全发批次的hr的批次id{},userId:{}", batchId, tokenUser.getUserId());
            throw new ParamException("安全发正在发放中，请勿重复提交");
        }
        try {
            TransferAccountsBatchDO transferAccountsBatchDO = this.selectStartBatch(batchId, tokenUser);
            LogKit.log("批次名为：" + transferAccountsBatchDO.getOrderTitle() + "，薪资发放(安全发)",
                    null, "oneKeyPay", batchId, tokenUser);
            // 查询安全发余额
            CertAlipayRequest certAlipayRequest = this.buildCertAlipaySafetyRequest();
            AlipayUserAgreementQueryResponse alipayUserAgreementQueryResponse = AliSafetyPay
                    .selectAgreementNo(certAlipayRequest, transferAccountInfoVO.getZfbLoginId());
            String agreementNo = alipayUserAgreementQueryResponse.getAgreementNo();
            AccountBookQueryVO accountBookQueryVO = AliSafetyPay.selectAccountBook(certAlipayRequest,
                    transferAccountInfoVO.getZfbAccountBookId(), agreementNo);
            BigDecimal money = new BigDecimal(accountBookQueryVO.getAvailableAmount());
            if (transferAccountsBatchDO.getMoney().compareTo(money) > 0) {
                throw new ParamException("记账本余额不足，请充值或选择其他记账本!");
            }
            // 根据批次id查询批次明细
            List<TransferAccountsDetailDO> transferAccountsDetailDOList = transferAccountsDetailService
                    .selectByBatchId(batchId, tokenUser.getSiteId());
            if (CollectionUtils.isEmpty(transferAccountsDetailDOList)) {
                throw new ParamException("根据批次id没有查询到批次的明细，创建批次订单失败！");
            }
            // 修改批次信息为发放中
            UserDO userDO = userProviderService.selectUserById(tokenUser.getUserId());
            ParamException.notNull(userDO, "当前用户不存在或已删除，请重新登录！");
            transferAccountsBatchDO.setIssueUserId(tokenUser.getUserId());
            transferAccountsBatchDO.setIssueUserName(userDO.getName());
            transferAccountsBatchDO.setIssueCreate(new Date());
            transferAccountsBatchDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.WAIT_ISSUE.getValue());
            transferAccountsBatchDO.setPayStatus(TransferAccountsPayStatusEnum.PAY_ING.getValue());
            transferAccountsBatchDO.setSecondType(TransferAccountsSecondTypeEnum.ZFB_SAFETY_PAY.getValue());
            transferAccountsBatchDO.setIssueAccountName(transferAccountInfoVO.getName());
            this.updateByIdSelective(transferAccountsBatchDO, tokenUser.getSiteId(), tokenUser.getUserId());
            // 异步调用安全发
            long startTime = System.currentTimeMillis();
            ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
            singleThreadExecutor.execute(() -> {
                int successCount = 0;
                int failCount = 0;
                BigDecimal successMoney = new BigDecimal(0);
                BigDecimal failMoney = new BigDecimal(0);
                for (TransferAccountsDetailDO transferAccountsDetailDO : transferAccountsDetailDOList) {
                    boolean safetyPayResult = zfbSafetyOneByDetail(transferAccountsDetailDO, certAlipayRequest,
                            accountBookQueryVO.getAccountBookId(), agreementNo, transferAccountsBatchDO.getOrderTitle(),
                            tokenUser);
                    // 创建安全发单笔转账订单
                    if (safetyPayResult) {
                        successCount++;
                        successMoney = successMoney.add(transferAccountsDetailDO.getMoney());
                    } else {
                        failCount++;
                        failMoney = failMoney.add(transferAccountsDetailDO.getMoney());
                    }
                }
                // 设置批次的相关信息
                transferAccountsBatchDO.setTransferCreate(new Date());
                int payStatus;
                if (failCount > 0) {
                    payStatus = TransferAccountsPayStatusEnum.ERROR_PAY.getValue();
                    transferAccountsBatchDO.setIsDispose(false);
                } else {
                    payStatus = TransferAccountsPayStatusEnum.SUCCESS_PAY.getValue();
                }
                transferAccountsBatchDO.setSuccessCount(successCount);
                transferAccountsBatchDO.setSuccessMoney(successMoney);
                transferAccountsBatchDO.setFailCount(failCount);
                transferAccountsBatchDO.setFailMoney(failMoney);
                transferAccountsBatchDO.setPayStatus(payStatus);
                transferAccountsBatchDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.YET_ISSUE.getValue());
                this.updateByIdSelective(transferAccountsBatchDO, tokenUser.getSiteId(), tokenUser.getUserId());
                log.info("安全发，批次id为：{}，耗时：{}毫秒",
                        transferAccountsBatchDO.getId(), System.currentTimeMillis() - startTime);
            });
            singleThreadExecutor.shutdown();
            return "success";
        } finally {
            //释放锁
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private boolean zfbSafetyOneByDetail(TransferAccountsDetailDO transferAccountsDetailDO,
                                         CertAlipayRequest certAlipayRequest,
                                         String accountBookId, String agreementNo,
                                         String orderTitle, TokenUser tokenUser) {
        String money = transferAccountsDetailDO.getMoney().toString();
        String remark = transferAccountsDetailDO.getRemark() == null ?
                orderTitle : transferAccountsDetailDO.getRemark();
        String name = transferAccountsDetailDO.getName();
        String identity = transferAccountsDetailDO.getIdentity();
        // 新增一条支付宝安全发明细记录
        TransferAccountsZfbSafetyDetailDO transferAccountsZfbSafetyDetailDO = new TransferAccountsZfbSafetyDetailDO();
        transferAccountsZfbSafetyDetailDO.setTransAmountAll(money);
        transferAccountsZfbSafetyDetailDO.setAccountBookId(accountBookId);
        transferAccountsZfbSafetyDetailDO.setAgreementNo(agreementNo);
        transferAccountsZfbSafetyDetailDO.setOrderTitle(orderTitle);
        transferAccountsZfbSafetyDetailDO.setRemark(remark);
        transferAccountsZfbSafetyDetailDO.setIdentityType("ALIPAY_LOGON_ID");
        transferAccountsZfbSafetyDetailDO.setIdentity(identity);
        transferAccountsZfbSafetyDetailDO.setName(name);
        transferAccountsZfbSafetyDetailDO = transferAccountsZfbSafetyDetailService.save(transferAccountsZfbSafetyDetailDO,
                tokenUser.getSiteId(), tokenUser.getUserId());
        // 创建
        SafetyPayOneVO safetyPayOneVO = new SafetyPayOneVO();
        safetyPayOneVO.setOutBizNo(transferAccountsZfbSafetyDetailDO.getId());
        safetyPayOneVO.setTransAmount(money);
        safetyPayOneVO.setOrderTitle(orderTitle);
        safetyPayOneVO.setRemark(remark);
        SafetyPayerInfoVO safetyPayerInfoVO = new SafetyPayerInfoVO();
        safetyPayerInfoVO.setExtInfo("{\"agreement_no\":\"" + agreementNo + "\"}");
        safetyPayerInfoVO.setIdentity(accountBookId);
        SafetyPayeeInfoVO safetyPayeeInfoVO = new SafetyPayeeInfoVO();
        safetyPayeeInfoVO.setName(name);
        safetyPayeeInfoVO.setIdentity(identity);
        safetyPayOneVO.setPayerInfo(safetyPayerInfoVO);
        safetyPayOneVO.setPayeeInfo(safetyPayeeInfoVO);
        SafetyPayResultVO safetyPayResultVO = AliSafetyPay.payOne(certAlipayRequest, safetyPayOneVO);

        transferAccountsZfbSafetyDetailDO.setStatus(safetyPayResultVO.getStatus());
        transferAccountsDetailDO.setThirdpartyDetailId(transferAccountsZfbSafetyDetailDO.getId());
        transferAccountsDetailDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.YET_ISSUE.getValue());
        if (StrKit.isNotEmpty(safetyPayResultVO.getSubMsg())) {
            // 修改hr批次明细信息
            transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
            transferAccountsDetailDO.setFailCause(safetyPayResultVO.getSubMsg());
            transferAccountsDetailDO.setIsDispose(false);
            transferAccountsDetailDO.setThirdpartyDetailId(transferAccountsZfbSafetyDetailDO.getId());
            transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO,
                    tokenUser.getSiteId(), tokenUser.getUserId());
            // 修改安全发批次明细信息
            transferAccountsZfbSafetyDetailDO.setFailReason(safetyPayResultVO.getSubMsg());
            transferAccountsZfbSafetyDetailDO.setErrorCode(safetyPayResultVO.getSubCode());
            transferAccountsZfbSafetyDetailService.updateByIdSelective(transferAccountsZfbSafetyDetailDO,
                    tokenUser.getSiteId(), tokenUser.getUserId());
            return false;
        }
        // 修改hr批次明细信息
        transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.SUCCESS_PAY.getValue());
        transferAccountsDetailDO.setThirdPartyInteriorDetailId(safetyPayResultVO.getOrderId());
        Date payTime = StrKit.isEmpty(safetyPayResultVO.getPayDate()) ?
                new Date() : DateKit8.parseDateTime(safetyPayResultVO.getPayDate());
        transferAccountsDetailDO.setPayTime(payTime);
        transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO,
                tokenUser.getSiteId(), tokenUser.getUserId());
        // 修改安全发批次明细信息
        transferAccountsZfbSafetyDetailDO.setOrderId(safetyPayResultVO.getOrderId());
        transferAccountsZfbSafetyDetailDO.setPayFundOrderId(safetyPayResultVO.getPayFundOrderId());
        transferAccountsZfbSafetyDetailDO.setPayDate(safetyPayResultVO.getPayDate());
        transferAccountsZfbSafetyDetailService.updateByIdSelective(transferAccountsZfbSafetyDetailDO,
                tokenUser.getSiteId(), tokenUser.getUserId());
        return true;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public String zfbSafetyStartTransferAccountsToBankCard(TransferStartZfbSafetyBatchQuery transferStartZfbSafetyBatchQuery, TokenUser tokenUser) {
        String accountId = transferStartZfbSafetyBatchQuery.getAccountId();
        String code = transferStartZfbSafetyBatchQuery.getCode();
        String batchId = transferStartZfbSafetyBatchQuery.getBatchId();
        // 查询账户
        TransferAccountInfoVO transferAccountInfoVO = transferAccountInfoService.selectById(accountId, tokenUser.getSiteId());
        ParamException.notNull(transferAccountInfoVO, "没有找到记账本账户，请刷新重试!");
        //校验短信验证码
        boolean check = messageProviderService.verifyCaptcha("zfb_safety", transferAccountInfoVO.getPhone(), code);
        if (!check) {
            throw new ParamException("验证码错误");
        }
        // 根据批次id加锁
        RLock lock = redissonClient.getLock(batchId);
        boolean hasLock;
        try {
            // 加锁，等待0秒，上锁以后1000秒自动解锁
            hasLock = lock.tryLock(0, 1000, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if (!hasLock) {
            log.warn("安全发批次的hr的批次id{},userId:{}", batchId, tokenUser.getUserId());
            throw new ParamException("安全发正在发放中，请勿重复提交");
        }
        try {
            TransferAccountsBatchDO transferAccountsBatchDO = this.selectStartBatch(batchId, tokenUser);
            LogKit.log("批次名为：" + transferAccountsBatchDO.getOrderTitle() + "，薪资发放(安全发)",
                    null, "oneKeyPay", batchId, tokenUser);
            // 查询安全发余额
            CertAlipayRequest certAlipayRequest = this.buildCertAlipaySafetyRequest();
            AlipayUserAgreementQueryResponse alipayUserAgreementQueryResponse = AliSafetyPay
                    .selectAgreementNo(certAlipayRequest, transferAccountInfoVO.getZfbLoginId());
            String agreementNo = alipayUserAgreementQueryResponse.getAgreementNo();
            AccountBookQueryVO accountBookQueryVO = AliSafetyPay.selectAccountBook(certAlipayRequest,
                    transferAccountInfoVO.getZfbAccountBookId(), agreementNo);
            BigDecimal money = new BigDecimal(accountBookQueryVO.getAvailableAmount());
            if (transferAccountsBatchDO.getMoney().compareTo(money) > 0) {
                throw new ParamException("记账本余额不足，请充值或选择其他记账本!");
            }
            // 根据批次id查询批次明细
            List<TransferAccountsDetailDO> transferAccountsDetailDOList = transferAccountsDetailService
                    .selectByBatchId(batchId, tokenUser.getSiteId());
            if (CollectionUtils.isEmpty(transferAccountsDetailDOList)) {
                throw new ParamException("根据批次id没有查询到批次的明细，创建批次订单失败！");
            }
            // 修改批次信息为发放中
            UserDO userDO = userProviderService.selectUserById(tokenUser.getUserId());
            ParamException.notNull(userDO, "当前用户不存在或已删除，请重新登录！");
            transferAccountsBatchDO.setIssueUserId(tokenUser.getUserId());
            transferAccountsBatchDO.setIssueUserName(userDO.getName());
            transferAccountsBatchDO.setIssueCreate(new Date());
            transferAccountsBatchDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.WAIT_ISSUE.getValue());
            transferAccountsBatchDO.setPayStatus(TransferAccountsPayStatusEnum.PAY_ING.getValue());
            transferAccountsBatchDO.setSecondType(TransferAccountsSecondTypeEnum.ZFB_SAFETY_PAY.getValue());
            transferAccountsBatchDO.setIssueAccountName(transferAccountInfoVO.getName());
            this.updateByIdSelective(transferAccountsBatchDO, tokenUser.getSiteId(), tokenUser.getUserId());
            // 异步调用安全发
            long startTime = System.currentTimeMillis();
            ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
            singleThreadExecutor.execute(() -> {
                for (TransferAccountsDetailDO transferAccountsDetailDO : transferAccountsDetailDOList) {
                    // 创建安全发单笔转账到卡订单
                    zfbSafetyOneToBankCardByDetail(transferAccountsDetailDO, certAlipayRequest,
                            accountBookQueryVO.getAccountBookId(), agreementNo, transferAccountsBatchDO.getOrderTitle(),
                            tokenUser);
                }
                log.info("安全发，批次id为：{}，耗时：{}毫秒",
                        transferAccountsBatchDO.getId(), System.currentTimeMillis() - startTime);
            });
            singleThreadExecutor.shutdown();
            return "success";
        } finally {
            //释放锁
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private boolean zfbSafetyOneToBankCardByDetail(TransferAccountsDetailDO transferAccountsDetailDO,
                                                   CertAlipayRequest certAlipayRequest,
                                                   String accountBookId, String agreementNo,
                                                   String orderTitle, TokenUser tokenUser) {
        String money = transferAccountsDetailDO.getMoney().toString();
        String remark = transferAccountsDetailDO.getRemark();
        if (transferAccountsDetailDO.getRemark() == null &&
                transferAccountsDetailDO.getMoney().compareTo(new BigDecimal("50000")) > 0) {
            remark = orderTitle;
        }
        String name = transferAccountsDetailDO.getName();
        String identity = transferAccountsDetailDO.getIdentity();
        // 新增一条支付宝安全发明细记录
        TransferAccountsZfbSafetyDetailDO transferAccountsZfbSafetyDetailDO = new TransferAccountsZfbSafetyDetailDO();
        transferAccountsZfbSafetyDetailDO.setTransAmountAll(money);
        transferAccountsZfbSafetyDetailDO.setAccountBookId(accountBookId);
        transferAccountsZfbSafetyDetailDO.setAgreementNo(agreementNo);
        transferAccountsZfbSafetyDetailDO.setOrderTitle(orderTitle);
        transferAccountsZfbSafetyDetailDO.setRemark(remark);
        transferAccountsZfbSafetyDetailDO.setIdentityType("BANKCARD_ACCOUNT");
        transferAccountsZfbSafetyDetailDO.setIdentity(identity);
        transferAccountsZfbSafetyDetailDO.setName(name);
        transferAccountsZfbSafetyDetailDO = transferAccountsZfbSafetyDetailService.save(transferAccountsZfbSafetyDetailDO,
                tokenUser.getSiteId(), tokenUser.getUserId());
        // 创建
        SafetyPayOneVO safetyPayOneVO = new SafetyPayOneVO();
        safetyPayOneVO.setOutBizNo(transferAccountsZfbSafetyDetailDO.getId());
        safetyPayOneVO.setTransAmount(money);
        safetyPayOneVO.setOrderTitle(orderTitle);
        safetyPayOneVO.setRemark(remark);
        SafetyPayerInfoVO safetyPayerInfoVO = new SafetyPayerInfoVO();
        safetyPayerInfoVO.setExtInfo("{\"agreement_no\":\"" + agreementNo + "\"}");
        safetyPayerInfoVO.setIdentity(accountBookId);
        SafetyPayeeInfoVO safetyPayeeInfoVO = new SafetyPayeeInfoVO();
        safetyPayeeInfoVO.setName(name);
        safetyPayeeInfoVO.setIdentity(identity);
        safetyPayOneVO.setPayerInfo(safetyPayerInfoVO);
        safetyPayOneVO.setPayeeInfo(safetyPayeeInfoVO);
        AlipayFundTransUniTransferResponse safetyPayToCardResultVO = AliSafetyPay.payOneToBankCard(certAlipayRequest, safetyPayOneVO);

        transferAccountsDetailDO.setThirdpartyDetailId(transferAccountsZfbSafetyDetailDO.getId());
        transferAccountsDetailDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.YET_ISSUE.getValue());
        if (StrKit.isNotEmpty(safetyPayToCardResultVO.getSubMsg())
                && StrKit.isEmpty(safetyPayToCardResultVO.getOrderId())) {
            // 修改hr批次明细信息
            transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
            transferAccountsDetailDO.setFailCause(safetyPayToCardResultVO.getSubMsg());
            transferAccountsDetailDO.setIsDispose(false);
            transferAccountsDetailDO.setThirdpartyDetailId(transferAccountsZfbSafetyDetailDO.getId());
            transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO,
                    tokenUser.getSiteId(), tokenUser.getUserId());
            return false;
        }
        // 修改hr批次明细信息
        transferAccountsDetailDO.setThirdPartyInteriorDetailId(safetyPayToCardResultVO.getOrderId());
        transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO,
                tokenUser.getSiteId(), tokenUser.getUserId());
        return true;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public String zfbSafetyBatchStartTransferAccounts(TransferStartZfbSafetyBatchQuery transferStartZfbSafetyBatchQuery, TokenUser tokenUser) {
        String accountId = transferStartZfbSafetyBatchQuery.getAccountId();
        String code = transferStartZfbSafetyBatchQuery.getCode();
        String batchId = transferStartZfbSafetyBatchQuery.getBatchId();
        // 查询账户
        TransferAccountInfoVO transferAccountInfoVO = transferAccountInfoService.selectById(accountId, tokenUser.getSiteId());
        ParamException.notNull(transferAccountInfoVO, "没有找到记账本账户，请刷新重试!");
        //校验短信验证码
        boolean check = messageProviderService.verifyCaptcha("zfb_safety", transferAccountInfoVO.getPhone(), code);
        if (!check) {
            throw new ParamException("验证码错误");
        }
        // 根据批次id加锁
        RLock lock = redissonClient.getLock(batchId);
        boolean hasLock;
        try {
            // 加锁，等待0秒，上锁以后1000秒自动解锁
            hasLock = lock.tryLock(0, 1000, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if (!hasLock) {
            log.warn("安全发批次的hr的批次id{},userId:{}", batchId, tokenUser.getUserId());
            throw new ParamException("安全发正在发放中，请勿重复提交");
        }
        try {
            TransferAccountsBatchDO transferAccountsBatchDO = this.selectStartBatch(batchId, tokenUser);
            LogKit.log("批次名为：" + transferAccountsBatchDO.getOrderTitle() + "，薪资发放(安全发)",
                    null, "oneKeyPay", batchId, tokenUser);
            // 查询安全发余额
            CertAlipayRequest certAlipayRequest = this.buildCertAlipaySafetyRequest();
            AlipayUserAgreementQueryResponse alipayUserAgreementQueryResponse = AliSafetyPay
                    .selectAgreementNo(certAlipayRequest, transferAccountInfoVO.getZfbLoginId());
            String agreementNo = alipayUserAgreementQueryResponse.getAgreementNo();
            AccountBookQueryVO accountBookQueryVO = AliSafetyPay.selectAccountBook(certAlipayRequest,
                    transferAccountInfoVO.getZfbAccountBookId(), agreementNo);
            BigDecimal money = new BigDecimal(accountBookQueryVO.getAvailableAmount());
            if (transferAccountsBatchDO.getMoney().compareTo(money) > 0) {
                throw new ParamException("记账本余额不足，请充值或选择其他记账本!");
            }
            // 根据批次id查询批次明细
            List<TransferAccountsDetailDO> transferAccountsDetailDOList = transferAccountsDetailService
                    .selectByBatchId(batchId, tokenUser.getSiteId());
            if (CollectionUtils.isEmpty(transferAccountsDetailDOList)) {
                throw new ParamException("根据批次id没有查询到批次的明细，创建批次订单失败！");
            }
            // 发起支付宝批量转账到户无密
            SafetyPayBatchVO safetyPayBatchVO = buildSafetyPayBatchVO(transferAccountsBatchDO,
                    transferAccountsDetailDOList, alipayUserAgreementQueryResponse, transferAccountInfoVO, tokenUser);
            // 调用支付宝安全发批量到账无密
            AlipayFundBatchUniTransferResponse alipayResponse = AliSafetyPay.payBatch(certAlipayRequest, safetyPayBatchVO);
            // 保存支付宝返回的支付宝批次订单号 到支付宝安全发的批次信息里
            TransferAccountsZfbSafetyBatchDO transferAccountsZfbSafetyBatchDO = new TransferAccountsZfbSafetyBatchDO();
            transferAccountsZfbSafetyBatchDO.setId(safetyPayBatchVO.getOutBatchNo());
            transferAccountsZfbSafetyBatchDO.setBatchTransId(alipayResponse.getBatchTransId());
            transferAccountsZfbSafetyBatchService.updateByIdSelective(transferAccountsZfbSafetyBatchDO,
                    tokenUser.getSiteId(), tokenUser.getUserId());
            transferAccountsBatchDO.setThirdPartyInteriorBatchId(alipayResponse.getBatchTransId());
            this.updateByIdSelective(transferAccountsBatchDO);
            return "success";
        } finally {
            //释放锁
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    // 保存支付宝相关的批次信息和明细，返回创建支付宝批次需要的JSON对象
    private SafetyPayBatchVO buildSafetyPayBatchVO(TransferAccountsBatchDO transferAccountsBatchDO,
                                                   List<TransferAccountsDetailDO> transferAccountsDetailDOList,
                                                   AlipayUserAgreementQueryResponse alipayUserAgreementQueryResponse,
                                                   TransferAccountInfoVO transferAccountInfoVO,
                                                   TokenUser tokenUser) {
        // 构造支付宝安全发批量到账无密的JSON对象
        SafetyPayBatchVO safetyPayBatchVO = new SafetyPayBatchVO();
        TransferAccountsZfbSafetyBatchVO transferAccountsZfbSafetyBatchVO = saveZfbSafetyBatch(tokenUser, transferAccountsBatchDO,
                safetyPayBatchVO, alipayUserAgreementQueryResponse, transferAccountInfoVO);

        List<SafetyPayBatchDetailVO> safetyPayBatchDetailVOList = new ArrayList<>();
        // 创建支付宝批次明细信息
        for (TransferAccountsDetailDO transferAccountsDetailDO : transferAccountsDetailDOList) {
            saveZfbSafetyDetail(transferAccountsZfbSafetyBatchVO, transferAccountsDetailDO,
                    safetyPayBatchDetailVOList, alipayUserAgreementQueryResponse.getAgreementNo(),
                    transferAccountInfoVO.getZfbAccountBookId(), tokenUser);
        }
        safetyPayBatchVO.setTransOrderList(safetyPayBatchDetailVOList);
        return safetyPayBatchVO;
    }

    /**
     * 根据hr批次信息创建支付宝安全发批次信息
     *
     * @param tokenUser                        当前用户
     * @param transferAccountsBatchDO          hr批次信息
     * @param safetyPayBatchVO                 支付宝安全发批次下单需要的JSON对象
     * @param alipayUserAgreementQueryResponse 查询到的商家协议号及uid
     * @param transferAccountInfoVO            安全发账户信息
     * @return 支付宝批次信息
     */
    private TransferAccountsZfbSafetyBatchVO saveZfbSafetyBatch(TokenUser tokenUser,
                                                                TransferAccountsBatchDO transferAccountsBatchDO,
                                                                SafetyPayBatchVO safetyPayBatchVO,
                                                                AlipayUserAgreementQueryResponse alipayUserAgreementQueryResponse,
                                                                TransferAccountInfoVO transferAccountInfoVO) {


        UserDO userDO = userProviderService.selectUserById(tokenUser.getUserId());
        ParamException.notNull(userDO, "当前用户不存在或已删除，请重新登录！");
        // 创建支付宝安全发批次表信息
        TransferAccountsZfbSafetyBatchAddDTO transferAccountsZfbSafetyBatchAddDTO = new TransferAccountsZfbSafetyBatchAddDTO();
        transferAccountsZfbSafetyBatchAddDTO.setBatchId(transferAccountsBatchDO.getId());
        transferAccountsZfbSafetyBatchAddDTO.setOrderTitle(transferAccountsBatchDO.getOrderTitle());
        transferAccountsZfbSafetyBatchAddDTO.setRemark(transferAccountsBatchDO.getRemark());
        transferAccountsZfbSafetyBatchAddDTO.setPayerUserId(alipayUserAgreementQueryResponse.getPrincipalId());
        String payerExtInfo = "{\"agreement_no\":\"" + alipayUserAgreementQueryResponse.getAgreementNo() +
                "\",\"account_book_id\":\"" + transferAccountInfoVO.getZfbAccountBookId() +
                "\",\"accountbook_scene_code\":\"SATF_FUND_BOOK\"}";
        transferAccountsZfbSafetyBatchAddDTO.setPayerExtInfo(payerExtInfo);
        transferAccountsZfbSafetyBatchAddDTO.setPayerBookId(transferAccountInfoVO.getZfbAccountBookId());
        TransferAccountsZfbSafetyBatchVO transferAccountsZfbSafetyBatchVO = transferAccountsZfbSafetyBatchService
                .save(transferAccountsZfbSafetyBatchAddDTO, tokenUser);
        // 设置批次的相关信息
        transferAccountsBatchDO.setThirdpartyBatchId(transferAccountsZfbSafetyBatchVO.getId());
        transferAccountsBatchDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.WAIT_ISSUE.getValue());
        transferAccountsBatchDO.setPayStatus(TransferAccountsPayStatusEnum.PAY_ING.getValue());
        transferAccountsBatchDO.setSecondType(TransferAccountsSecondTypeEnum.ZFB_SAFETY_PAY.getValue());
        transferAccountsBatchDO.setIssueUserId(tokenUser.getUserId());
        transferAccountsBatchDO.setIssueUserName(userDO.getName());
        transferAccountsBatchDO.setIssueCreate(new Date());
        transferAccountsBatchDO.setIssueAccountName(transferAccountInfoVO.getName());
        // 修改批次信息
        this.updateByIdSelective(transferAccountsBatchDO, tokenUser.getSiteId(), tokenUser.getUserId());

        // 使用第三方表的id 作为此次在支付宝创建的批次id
        safetyPayBatchVO.setOutBatchNo(transferAccountsZfbSafetyBatchVO.getId());
        safetyPayBatchVO.setOrderTitle(transferAccountsBatchDO.getOrderTitle());
        safetyPayBatchVO.setTotalTransAmount(transferAccountsBatchDO.getMoney().doubleValue());
        safetyPayBatchVO.setTotalCount(transferAccountsBatchDO.getTotalCount() + "");
        safetyPayBatchVO.setRemark(transferAccountsBatchDO.getRemark());
        SafetyPayerInfoVO safetyPayerInfoVO = new SafetyPayerInfoVO();
        safetyPayerInfoVO.setIdentity(alipayUserAgreementQueryResponse.getPrincipalId());
        safetyPayerInfoVO.setExtInfo(payerExtInfo);
        safetyPayBatchVO.setPayerInfo(safetyPayerInfoVO);
        safetyPayBatchVO.setBusinessParams("{\"agreement_no\":\"" + alipayUserAgreementQueryResponse.getAgreementNo() +
                "\",\"fill_remark_of_staf_acct\":\"false\"}");
        return transferAccountsZfbSafetyBatchVO;
    }

    /**
     * 根据hr的批次明细信息创建支付宝的安全发批次明细信息
     *
     * @param transferAccountsZfbSafetyBatchVO 支付宝的安全发批次信息
     * @param transferAccountsDetailDO         hr的批次明细信息
     * @param tokenUser                        当前用户
     * @param safetyPayBatchDetailVOList       支付宝安全发批次下单需要的JSON对象List
     */
    private void saveZfbSafetyDetail(TransferAccountsZfbSafetyBatchVO transferAccountsZfbSafetyBatchVO,
                                     TransferAccountsDetailDO transferAccountsDetailDO,
                                     List<SafetyPayBatchDetailVO> safetyPayBatchDetailVOList,
                                     String agreementNo, String accountBookId, TokenUser tokenUser) {
        TransferAccountsZfbSafetyDetailAddDTO transferAccountsZfbSafetyDetailAddDTO = new TransferAccountsZfbSafetyDetailAddDTO();
        transferAccountsZfbSafetyDetailAddDTO.setSafetyBatchId(transferAccountsZfbSafetyBatchVO.getId());
        transferAccountsZfbSafetyDetailAddDTO.setName(transferAccountsDetailDO.getName());
        transferAccountsZfbSafetyDetailAddDTO.setIdentity(transferAccountsDetailDO.getIdentity());
        transferAccountsZfbSafetyDetailAddDTO.setIdentityType("ALIPAY_LOGON_ID");
        transferAccountsZfbSafetyDetailAddDTO.setTransAmountAll(transferAccountsDetailDO.getMoney() + "");
        transferAccountsZfbSafetyDetailAddDTO.setOrderTitle(transferAccountsDetailDO.getOrderTitle());
        transferAccountsZfbSafetyDetailAddDTO.setRemark(transferAccountsDetailDO.getRemark());
        transferAccountsZfbSafetyDetailAddDTO.setAgreementNo(agreementNo);
        transferAccountsZfbSafetyDetailAddDTO.setAccountBookId(accountBookId);
        TransferAccountsZfbSafetyDetailVO transferAccountsZfbSafetyDetailVO = transferAccountsZfbSafetyDetailService
                .save(transferAccountsZfbSafetyDetailAddDTO, tokenUser);
        // 修改明细表信息
        transferAccountsDetailDO.setThirdpartyDetailId(transferAccountsZfbSafetyDetailVO.getId());
        transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO, tokenUser.getSiteId(),
                tokenUser.getUserId());

        // 构建收款明细信息JSON
        SafetyPayBatchDetailVO safetyPayBatchDetailVO = new SafetyPayBatchDetailVO();
        safetyPayBatchDetailVO.setOutBizNo(transferAccountsZfbSafetyDetailVO.getId());
        if (transferAccountsDetailDO.getMoney() == null) {
            throw new ParamException("单个收款信息金额不能为空，请检查是否有没有填写的收款金额！");
        }
        safetyPayBatchDetailVO.setTransAmount(transferAccountsDetailDO.getMoney().toString());
        safetyPayBatchDetailVO.setRemark(transferAccountsDetailDO.getRemark());
        SafetyPayeeInfoVO safetyPayeeInfoVO = new SafetyPayeeInfoVO();
        if (StrKit.isEmpty(transferAccountsDetailDO.getIdentity())) {
            throw new ParamException("收款账号不能为空，请检查是否有没有填写的收款账号(登录支付宝的账号名：邮箱或手机号格式)");
        }
        safetyPayeeInfoVO.setIdentity(transferAccountsDetailDO.getIdentity());
        safetyPayeeInfoVO.setIdentityType("ALIPAY_LOGON_ID");
        if (StrKit.isEmpty(transferAccountsDetailDO.getName())) {
            throw new ParamException("收款真实姓名不能为空，请检查是否有没有填写");
        }
        safetyPayeeInfoVO.setName(transferAccountsDetailDO.getName());
        safetyPayBatchDetailVO.setPayeeInfo(safetyPayeeInfoVO);
        safetyPayBatchDetailVOList.add(safetyPayBatchDetailVO);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void vxStartTransferAccounts(TransferStartVxBatchQuery transferStartVxBatchQuery, TokenUser tokenUser) {

        String batchId = transferStartVxBatchQuery.getBatchId();
        String subMchid = transferStartVxBatchQuery.getSubMchid();
        // 根据批次id加锁
        RLock lock = redissonClient.getLock(batchId);
        boolean hasLock;
        try {
            // 加锁，等待0秒，上锁以后300秒自动解锁
            hasLock = lock.tryLock(0, 300, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if (!hasLock) {
            log.warn("微信创建批次的hr的批次id{},userId:{}", batchId, tokenUser.getUserId());
            throw new ParamException("一个批次只能提交一次，请勿重复提交");
        }
        try {
            TransferAccountsBatchDO transferAccountsBatchDO = this.selectStartBatch(batchId, tokenUser);
            LogKit.log("批次名为：" + transferAccountsBatchDO.getOrderTitle() + "，薪资发放",
                    null, "oneKeyPay", batchId, tokenUser);
            // 根据批次id查询批次明细
            List<TransferAccountsDetailDO> transferAccountsDetailDOList = transferAccountsDetailService
                    .selectByBatchId(batchId, tokenUser.getSiteId());
            if (CollectionUtils.isEmpty(transferAccountsDetailDOList)) {
                throw new ParamException("根据批次id没有查询到批次的明细，创建批次订单失败！");
            }
            // 获取微信服务商全部的配置信息
            VxConfigVO vxConfigVO = this.selectVxConfigVO(tokenUser.getSiteId());
            // 构造微信批次需要的参数对象
            VxBatchOrderVO vxBatchOrderVO = new VxBatchOrderVO();
            TransferAccountsVxBatchVO transferAccountsVxBatchVO = this
                    .saveVxBatch(tokenUser, vxConfigVO.getSpMchid(), subMchid,
                            vxConfigVO.getSpAppid(), transferAccountsBatchDO, vxBatchOrderVO);
            List<VxTransOrderVO> vxTransOrderVOList = new ArrayList<>();
            // 创建微信批次明细信息
            for (TransferAccountsDetailDO transferAccountsDetailDO : transferAccountsDetailDOList) {
                saveVxDetail(transferAccountsVxBatchVO, transferAccountsDetailDO,
                        tokenUser, vxConfigVO.getVxCertificate(), vxTransOrderVOList);
            }
            vxBatchOrderVO.setVxTransOrderVOList(vxTransOrderVOList);

            // 获取微信请求头的Authorization
            String authorization = VXpayClear.generateAuthorization(JSON.toJSONString(vxBatchOrderVO),
                    "POST", "https://api.mch.weixin.qq.com/v3/partner-transfer/batches",
                    vxConfigVO.getSpMchid(), vxConfigVO.getCertificateSerialNo(), vxConfigVO.getPrivateKeyPath());
            // 发送微信批次创建订单的请求
            VxBatchResponseVO vxBatchResponseVO = VXpayClear.vxBatchStart(vxBatchOrderVO, authorization,
                    vxConfigVO.getWechatpaySerial());
            if (vxBatchResponseVO == null || StrKit.isEmpty(vxBatchResponseVO.getBatchId())) {
                throw new ParamException("生成微信批次失败，请联系管理员查询原因!");
            }
            String vxBatchId = vxBatchResponseVO.getBatchId();
            // 保存微信内部批次id到hr批次表、微信批次表
            TransferAccountsVxBatchDO transferAccountsVxBatchDO = new TransferAccountsVxBatchDO();
            transferAccountsVxBatchDO.setId(transferAccountsBatchDO.getThirdpartyBatchId());
            transferAccountsVxBatchDO.setBatchId(vxBatchId);
            transferAccountsVxBatchService.updateByIdSelective(transferAccountsVxBatchDO,
                    tokenUser.getSiteId(), tokenUser.getUserId());
            transferAccountsBatchDO.setThirdPartyInteriorBatchId(vxBatchId);
            this.updateByIdSelective(transferAccountsBatchDO);
        } finally {
            //释放锁
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 根据hr批次信息创建微信批次信息
     *
     * @param tokenUser               当前用户
     * @param spMchid                 服务商 商户号（系统级别）
     * @param subMchid                特约商户 商户号（租户级别）
     * @param spAppid                 服务商或者商户的appid
     * @param transferAccountsBatchDO hr批次信息
     * @param vxBatchOrderVO          微信批次下单需要的JSON对象
     * @return 微信批次信息
     */
    private TransferAccountsVxBatchVO saveVxBatch(TokenUser tokenUser, String spMchid,
                                                  String subMchid, String spAppid,
                                                  TransferAccountsBatchDO transferAccountsBatchDO,
                                                  VxBatchOrderVO vxBatchOrderVO) {
        UserDO userDO = userProviderService.selectUserById(tokenUser.getUserId());
        ParamException.notNull(userDO, "当前用户不存在或已删除，请重新登录！");
        // 新增微信批次信息
        TransferAccountsVxBatchAddDTO transferAccountsVxBatchAddDTO = new TransferAccountsVxBatchAddDTO();
        transferAccountsVxBatchAddDTO.setSpMchid(spMchid);
        transferAccountsVxBatchAddDTO.setSubMchid(subMchid);
        transferAccountsVxBatchAddDTO.setBatchName(transferAccountsBatchDO.getOrderTitle());
        transferAccountsVxBatchAddDTO.setBatchRemark(transferAccountsBatchDO.getRemark());
        // 单位：分
        int totalAmount = transferAccountsBatchDO.getMoney().multiply(new BigDecimal("100")).intValue();
        transferAccountsVxBatchAddDTO.setTotalAmount(totalAmount);
        transferAccountsVxBatchAddDTO.setTotalNum(transferAccountsBatchDO.getTotalCount());
        TransferAccountsVxBatchVO transferAccountsVxBatchVO = transferAccountsVxBatchService
                .save(transferAccountsVxBatchAddDTO, tokenUser);

        // 设置批次的相关信息
        transferAccountsBatchDO.setThirdpartyBatchId(transferAccountsVxBatchVO.getId());
        transferAccountsBatchDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.WAIT_ISSUE.getValue());
        transferAccountsBatchDO.setPayStatus(TransferAccountsPayStatusEnum.PAY_ING.getValue());
        transferAccountsBatchDO.setIssueUserId(tokenUser.getUserId());
        transferAccountsBatchDO.setIssueUserName(userDO.getName());
        transferAccountsBatchDO.setIssueCreate(new Date());
        // 根据pid 查询账户信息
        TransferAccountInfoDO transferAccountInfoDO = transferAccountInfoService
                .selectBySiteIdAndAccount(TransferAccountsTypeEnum.VX.getValue(), subMchid, tokenUser.getSiteId());
        String accountName = transferAccountInfoDO == null ? subMchid : transferAccountInfoDO.getName();
        transferAccountsBatchDO.setIssueAccountName(accountName);
        this.updateByIdSelective(transferAccountsBatchDO, tokenUser.getSiteId(), tokenUser.getUserId());

        // 使用第三方表的id 作为此次在微信创建的批次id
        vxBatchOrderVO.setOutBatchNo(transferAccountsVxBatchVO.getId());
        vxBatchOrderVO.setSubMchid(subMchid);
        vxBatchOrderVO.setAuthorizationType("FUND_AUTHORIZATION_TYPE");
        vxBatchOrderVO.setBatchName(transferAccountsBatchDO.getOrderTitle());
        // 微信备注必填
        String remark = transferAccountsBatchDO.getRemark();
        if (StrKit.isEmpty(remark)) {
            remark = transferAccountsBatchDO.getOrderTitle();
        }
        vxBatchOrderVO.setBatchRemark(remark);
        vxBatchOrderVO.setTotalAmount(totalAmount);
        vxBatchOrderVO.setTotalNum(transferAccountsBatchDO.getTotalCount());
        vxBatchOrderVO.setSpAppid(spAppid);
        vxBatchOrderVO.setTransferScene("PAYROLL_CARD_TRANSFER");
        return transferAccountsVxBatchVO;
    }

    /**
     * 根据hr的批次明细信息创建微信的批次明细信息
     *
     * @param transferAccountsVxBatchVO 微信的批次信息
     * @param transferAccountsDetailDO  hr的批次明细信息
     * @param tokenUser                 当前用户
     * @param vxCertificate             微信平台证书的路径
     * @param vxTransOrderVOList        微信批次下单需要的JSON对象List
     */
    private void saveVxDetail(TransferAccountsVxBatchVO transferAccountsVxBatchVO,
                              TransferAccountsDetailDO transferAccountsDetailDO,
                              TokenUser tokenUser, String vxCertificate,
                              List<VxTransOrderVO> vxTransOrderVOList) {
        TransferAccountsVxDetailAddDTO transferAccountsVxDetailAddDTO = new TransferAccountsVxDetailAddDTO();
        transferAccountsVxDetailAddDTO.setVxBatchId(transferAccountsVxBatchVO.getId());
        transferAccountsVxDetailAddDTO.setSpMchid(transferAccountsVxBatchVO.getSpMchid());
        // 金额单位为分 需要乘100
        if (transferAccountsDetailDO.getMoney() == null) {
            throw new ParamException("单个收款信息金额不能为空，请检查是否有没有填写的收款金额！");
        }
        int transferAmount = transferAccountsDetailDO.getMoney().multiply(new BigDecimal("100")).intValue();
        transferAccountsVxDetailAddDTO.setTransferAmount(transferAmount);
        transferAccountsVxDetailAddDTO.setTransferRemark(transferAccountsDetailDO.getRemark());
        if (StrKit.isEmpty(transferAccountsDetailDO.getIdentity())) {
            throw new ParamException("收款账号不能为空，请检查是否有没有填写的收款账号(微信)");
        }
        transferAccountsVxDetailAddDTO.setOpenid(transferAccountsDetailDO.getIdentity());
        // 加密姓名
        if (StrKit.isEmpty(transferAccountsDetailDO.getName())) {
            throw new ParamException("收款真实姓名不能为空，请检查是否有没有填写");
        }
        String name = transferAccountsDetailDO.getName();
        String encryptName = VXpayClear.rsaEncryptOAEP(name, vxCertificate);
        transferAccountsVxDetailAddDTO.setUsername(encryptName);
        TransferAccountsVxDetailVO transferAccountsVxDetailVO = transferAccountsVxDetailService
                .save(transferAccountsVxDetailAddDTO, tokenUser);
        // 修改明细表信息
        transferAccountsDetailDO.setThirdpartyDetailId(transferAccountsVxDetailVO.getId());
        transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO, tokenUser.getSiteId(),
                tokenUser.getUserId());
        // 构建收款明细信息JSON
        VxTransOrderVO vxTransOrderVO = new VxTransOrderVO();
        vxTransOrderVO.setOutDetailNo(transferAccountsVxDetailVO.getId());
        vxTransOrderVO.setTransferAmount(transferAmount);
        // 微信备注必填
        String remark = transferAccountsDetailDO.getRemark();
        if (StrKit.isEmpty(remark)) {
            remark = transferAccountsVxBatchVO.getBatchName();
        }
        vxTransOrderVO.setTransferRemark(remark);
        vxTransOrderVO.setOpenid(transferAccountsDetailDO.getIdentity());
        vxTransOrderVO.setUserName(encryptName);
        vxTransOrderVOList.add(vxTransOrderVO);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void pingAnStartTransferAccounts(TransferStartPingAnBankBatchQuery transferStartPingAnBankBatchQuery, TokenUser tokenUser) {
        String batchId = transferStartPingAnBankBatchQuery.getBatchId();
        String accountId = transferStartPingAnBankBatchQuery.getAccountId();
        String code = transferStartPingAnBankBatchQuery.getCode();
        // 查询账户
        TransferAccountInfoVO transferAccountInfoVO = transferAccountInfoService.selectById(accountId, tokenUser.getSiteId());
        ParamException.notNull(transferAccountInfoVO, "没有找到银行账户，请刷新重试!");
        ParamException.hasText(transferAccountInfoVO.getBankAccountId(), "发放账户银行账号不能为空!");
        ParamException.hasText(transferAccountInfoVO.getCompanyCode(), "企业银企直连标准代码不能为空!");
        ParamException.hasText(transferAccountInfoVO.getAgreeNo(), "委托单位协议号不能为空!");
        ParamException.hasText(transferAccountInfoVO.getBusiType(), "费项代码不能为空!");
        //校验短信验证码
        boolean check = messageProviderService.verifyCaptcha("zfb_safety", transferAccountInfoVO.getPhone(), code);
        if (!check) {
            throw new ParamException("验证码错误");
        }
        // 根据批次id加锁
        RLock lock = redissonClient.getLock(batchId);
        boolean hasLock;
        try {
            // 加锁，等待0秒，上锁以后300秒自动解锁
            hasLock = lock.tryLock(0, 300, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if (!hasLock) {
            log.warn("微信创建批次的hr的批次id{},userId:{}", batchId, tokenUser.getUserId());
            throw new ParamException("一个批次只能提交一次，请勿重复提交");
        }
        try {
            TransferAccountsBatchDO transferAccountsBatchDO = this.selectStartBatch(batchId, tokenUser);
            LogKit.log("批次名为：" + transferAccountsBatchDO.getOrderTitle() + "，薪资发放",
                    null, "oneKeyPay", batchId, tokenUser);
            // 根据批次id查询批次明细
            List<TransferAccountsDetailDO> transferAccountsDetailDOList = transferAccountsDetailService
                    .selectByBatchId(batchId, tokenUser.getSiteId());
            if (CollectionUtils.isEmpty(transferAccountsDetailDOList)) {
                throw new ParamException("根据批次id没有查询到批次的明细，创建批次订单失败！");
            }
            // 构造请求报文
            String bankBatchId = PingAnBankPay.getGuid();
            HrOneKeyPayProperties.PingAn pingAn = hrOneKeyPayProperties.getPingAn();
            PingAnBankTransferVO pingAnBankTransferVO = new PingAnBankTransferVO();
            TransferAccountsBankBatchVO transferAccountsBankBatchVO = savePingAnBankBatch(pingAnBankTransferVO,
                    bankBatchId, transferAccountInfoVO, tokenUser, transferAccountsBatchDO);
            List<HOResultSet4047RVO> hoResultSet4047RVOList = new ArrayList<>();
            int detailSortNum = 1;
            // 创建微信批次明细信息
            for (TransferAccountsDetailDO transferAccountsDetailDO : transferAccountsDetailDOList) {
                String detailThirdVoucher = bankBatchId.substring(4, 17) + detailSortNum;
                savePingAnBankDetail(transferAccountsBankBatchVO, transferAccountsDetailDO, tokenUser,
                        detailThirdVoucher, hoResultSet4047RVOList);
                detailSortNum++;
            }
            pingAnBankTransferVO.setHOResultSet4047R(hoResultSet4047RVOList);
            String xmlStr = PingAnBankPay.convertToXml(pingAnBankTransferVO, "GBK");
            String messageLength = String.format("%010d", xmlStr.getBytes("GBK").length);
            String dealCode = "4047  ";
            String serveType = "01";
            Date date = new Date();
            String dealDate = DateKit8.format(date, "yyyyMMdd");
            String dealTime = DateKit8.format(date, "HHmmss");
            // 构造请求报文头
            String requestHeader = pingAn.getMessageType() + pingAn.getTargetSystemNumber() +
                    pingAn.getMessageEncoder() + pingAn.getMessageAgreement() +
                    transferAccountInfoVO.getCompanyCode() + messageLength + dealCode + pingAn.getOperatorCode() +
                    serveType + dealDate + dealTime + bankBatchId +
                    "0000000000000000000000000000000000000000000000000000000000000000000000000000000" +
                    "00000000000000000000000000000000000000000000000000000000";
            String returnStr = PingAnBankPay.tcpPost("127.0.0.1", 7073, requestHeader + xmlStr);
            log.info("创建平安银行批次转账接口返回结果:{},===入参:{}", returnStr, requestHeader + xmlStr);
            if (!returnStr.contains("<?xml")) {
                log.error("批次id：{} 发起转账失败，平安银行返回结果：{}", batchId, returnStr);
                throw new ParamException("发起平安银行转账批次失败，请联系管理员查询原因!");
            }
            returnStr = returnStr.substring(returnStr.indexOf("<?xml"));
            ReturnHOResultSet4047RVO returnHOResultSet4047RVO = PingAnBankPay.converyToReturnHOResultSet4047RVO(returnStr,
                    ReturnHOResultSet4047RVO.class);
            if (returnHOResultSet4047RVO == null || StrKit.isEmpty(returnHOResultSet4047RVO.getBussFlowNo())) {
                log.error("批次id：{} 发起转账失败，平安银行返回结果：{}", batchId, returnStr);
                throw new ParamException("发起平安银行转账批次失败，请联系管理员查询原因!");
            }
            // 保存银行生成的业务流水号到hr批次表、银行批次表
            String bussFlowNo = returnHOResultSet4047RVO.getBussFlowNo();
            TransferAccountsBankBatchDO transferAccountsBankBatchDO = new TransferAccountsBankBatchDO();
            transferAccountsBankBatchDO.setId(transferAccountsBankBatchVO.getId());
            transferAccountsBankBatchDO.setBussFlowNo(bussFlowNo);
            transferAccountsBankBatchService.updateByIdSelective(transferAccountsBankBatchDO,
                    tokenUser.getSiteId(), tokenUser.getUserId());
            transferAccountsBatchDO.setThirdPartyInteriorBatchId(bussFlowNo);
            this.updateByIdSelective(transferAccountsBatchDO);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ParamException(e.getMessage());
        } finally {
            //释放锁
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 根据hr批次信息创建平安银行批次信息
     *
     * @param pingAnBankTransferVO 平安银行创建批次需要的信息
     * @param bankBatchId 凭证号（hr系统生成）
     * @param transferAccountInfoVO 发放账户信息
     * @param tokenUser 当前用户
     * @param transferAccountsBatchDO hr批次信息
     * @return 微信批次信息
     */
    private TransferAccountsBankBatchVO savePingAnBankBatch(PingAnBankTransferVO pingAnBankTransferVO, String bankBatchId,
                                                            TransferAccountInfoVO transferAccountInfoVO,
                                                            TokenUser tokenUser, TransferAccountsBatchDO transferAccountsBatchDO) {
        UserDO userDO = userProviderService.selectUserById(tokenUser.getUserId());
        ParamException.notNull(userDO, "当前用户不存在或已删除，请重新登录！");
        String payType = "1";
        String currency = "RMB";
        String bankAccountId = transferAccountInfoVO.getBankAccountId();
        String totalNum = transferAccountsBatchDO.getTotalCount() + "";
        String totalAmount = transferAccountsBatchDO.getMoney() + "";
        String settleType = "1";
        String remark = transferAccountsBatchDO.getRemark();
        String agreeNo = transferAccountInfoVO.getAgreeNo();
        String busiType = transferAccountInfoVO.getBusiType();
        // 新增微信批次信息
        TransferAccountsBankBatchAddDTO transferAccountsBankBatchAddDTO = new TransferAccountsBankBatchAddDTO();
        transferAccountsBankBatchAddDTO.setBatchId(transferAccountsBatchDO.getId());
        transferAccountsBankBatchAddDTO.setThirdVoucher(bankBatchId);
        transferAccountsBankBatchAddDTO.setAgreeNo(agreeNo);
        transferAccountsBankBatchAddDTO.setBusiType(busiType);
        transferAccountsBankBatchAddDTO.setPayType(payType);
        transferAccountsBankBatchAddDTO.setCurrency(currency);
        transferAccountsBankBatchAddDTO.setSrcAccNo(bankAccountId);
        transferAccountsBankBatchAddDTO.setTotalNum(totalNum);
        transferAccountsBankBatchAddDTO.setTotalAmount(totalAmount);
        transferAccountsBankBatchAddDTO.setSettleType(settleType);
        transferAccountsBankBatchAddDTO.setBatchOrdRemark(remark);
        TransferAccountsBankBatchVO transferAccountsBankBatchVO = transferAccountsBankBatchService
                .save(transferAccountsBankBatchAddDTO, tokenUser);

        // 设置批次的相关信息
        transferAccountsBatchDO.setThirdpartyBatchId(transferAccountsBankBatchVO.getId());
        transferAccountsBatchDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.WAIT_ISSUE.getValue());
        transferAccountsBatchDO.setPayStatus(TransferAccountsPayStatusEnum.PAY_ING.getValue());
        transferAccountsBatchDO.setIssueUserId(tokenUser.getUserId());
        transferAccountsBatchDO.setIssueUserName(userDO.getName());
        transferAccountsBatchDO.setIssueCreate(new Date());
        transferAccountsBatchDO.setTransferCreate(new Date());
        // 根据pid 查询账户信息
        TransferAccountInfoDO transferAccountInfoDO = transferAccountInfoService
                .selectBySiteIdAndAccount(TransferAccountsTypeEnum.BANK.getValue(), bankAccountId, tokenUser.getSiteId());
        String accountName = transferAccountInfoDO == null ? bankAccountId : transferAccountInfoDO.getName();
        transferAccountsBatchDO.setIssueAccountName(accountName);
        this.updateByIdSelective(transferAccountsBatchDO, tokenUser.getSiteId(), tokenUser.getUserId());

        // 构造平安银行发起批次信息
        pingAnBankTransferVO.setThirdVoucher(bankBatchId);
        pingAnBankTransferVO.setAGREE_NO(agreeNo);
        pingAnBankTransferVO.setBusiType(busiType);
        pingAnBankTransferVO.setPayType(payType);
        pingAnBankTransferVO.setCurrency(currency);
        pingAnBankTransferVO.setSrcAccNo(bankAccountId);
        pingAnBankTransferVO.setTotalNum(totalNum);
        pingAnBankTransferVO.setTotalAmount(totalAmount);
        pingAnBankTransferVO.setSettleType(settleType);
        pingAnBankTransferVO.setBatchOrdRemark(remark);
        return transferAccountsBankBatchVO;
    }

    /**
     * 根据hr的批次明细信息创建平安银行的批次明细信息
     *
     * @param transferAccountsBankBatchVO 平银行批次信息
     * @param transferAccountsDetailDO hr的批次明细信息
     * @param tokenUser 当前用户
     * @param detailThirdVoucher 平安银行批次明细流水号（hr系统定义）
     * @param hoResultSet4047RVOList 平安银行批次下单需要的明细对象List
     */
    private void savePingAnBankDetail(TransferAccountsBankBatchVO transferAccountsBankBatchVO,
                                      TransferAccountsDetailDO transferAccountsDetailDO,
                                      TokenUser tokenUser, String detailThirdVoucher,
                                      List<HOResultSet4047RVO> hoResultSet4047RVOList) {
        String othBankFlag = transferAccountsDetailDO.getIsPingAnBank() == null ?
                null : transferAccountsDetailDO.getIsPingAnBank() ?
                "N" : "Y";
        if (StrKit.isEmpty(transferAccountsDetailDO.getIdentity())) {
            throw new ParamException("收款账号不能为空，请检查是否有没有填写的收款账号");
        }
        String oppAccNo = transferAccountsDetailDO.getIdentity();
        if (StrKit.isEmpty(transferAccountsDetailDO.getName())) {
            throw new ParamException("收款户名不能为空，请检查是否有没有填写");
        }
        String oppAccName = transferAccountsDetailDO.getName();
        if (transferAccountsDetailDO.getMoney() == null) {
            throw new ParamException("单个收款信息金额不能为空，请检查是否有没有填写的收款金额！");
        }
        String amount = transferAccountsDetailDO.getMoney().toString();
        String postScript = transferAccountsDetailDO.getRemark();
        TransferAccountsBankDetailAddDTO transferAccountsBankDetailAddDTO = new TransferAccountsBankDetailAddDTO();
        transferAccountsBankDetailAddDTO.setBankBatchId(transferAccountsBankBatchVO.getId());
        transferAccountsBankDetailAddDTO.setThirdVoucher(transferAccountsBankBatchVO.getThirdVoucher());
        transferAccountsBankDetailAddDTO.setSThirdVoucher(detailThirdVoucher);
        transferAccountsBankDetailAddDTO.setOthBankFlag(othBankFlag);
        transferAccountsBankDetailAddDTO.setOppAccNo(oppAccNo);
        transferAccountsBankDetailAddDTO.setOppAccName(oppAccName);
        transferAccountsBankDetailAddDTO.setAmount(amount);
        transferAccountsBankDetailAddDTO.setPostScript(postScript);
        TransferAccountsBankDetailVO transferAccountsBankDetailVO = transferAccountsBankDetailService
                .save(transferAccountsBankDetailAddDTO, tokenUser);
        // 修改明细表信息
        transferAccountsDetailDO.setThirdpartyDetailId(transferAccountsBankDetailVO.getId());
        transferAccountsDetailDO.setThirdPartyInteriorDetailId(detailThirdVoucher);
        transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.NO_PAY.getValue());
        transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO, tokenUser.getSiteId(),
                tokenUser.getUserId());
        // 构建收款明细信息XML
        HOResultSet4047RVO hoResultSet4047RVO = new HOResultSet4047RVO();
        hoResultSet4047RVO.setSThirdVoucher(detailThirdVoucher);
        hoResultSet4047RVO.setOppAccNo(oppAccNo);
        hoResultSet4047RVO.setOppAccName(oppAccName);
        hoResultSet4047RVO.setAmount(amount);
        hoResultSet4047RVO.setOthBankFlag(othBankFlag);
        hoResultSet4047RVO.setPostScript(postScript);
        hoResultSet4047RVOList.add(hoResultSet4047RVO);
    }

    @Override
    public CertAlipayRequest buildCertAlipayRequest() {
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        // 查询配置参数
        String url = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.URL, null);
        if (StrKit.isEmpty(url)) {
            throw new ParamException("没有配置 批量转账使用的支付宝网关");
        }
        certAlipayRequest.setServerUrl(url);

        String appId = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.APPID, null);
        if (StrKit.isEmpty(appId)) {
            throw new ParamException("没有配置 批量转账使用的APPID");
        }
        certAlipayRequest.setAppId(appId);

        String appPrivateKey = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.APP_PRIVATE_KEY, null);
        if (StrKit.isEmpty(appPrivateKey)) {
            throw new ParamException("没有配置 批量转账使用的APP_PRIVATE_KEY");
        }
        certAlipayRequest.setPrivateKey(appPrivateKey);

        String appCertPath = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.APP_CERT_PATH, null);
        if (StrKit.isEmpty(appCertPath)) {
            throw new ParamException("没有配置 批量转账使用的APP_CERT_PATH");
        }
        certAlipayRequest.setCertPath(appCertPath);

        String alipayCertPath = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.ALIPAY_CERT_PATH, null);
        if (StrKit.isEmpty(alipayCertPath)) {
            throw new ParamException("没有配置 批量转账使用的ALIPAY_CERT_PATH");
        }
        certAlipayRequest.setAlipayPublicCertPath(alipayCertPath);

        String alipayRootCertPath = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.ALIPAY_ROOT_CERT_PATH, null);
        if (StrKit.isEmpty(alipayRootCertPath)) {
            throw new ParamException("没有配置 批量转账使用的ALIPAY_ROOT_CERT_PATH");
        }
        certAlipayRequest.setRootCertPath(alipayRootCertPath);
        certAlipayRequest.setFormat("JSON");
        certAlipayRequest.setCharset("UTF-8");
        certAlipayRequest.setSignType("RSA2");
        return certAlipayRequest;
    }

    @Override
    public CertAlipayRequest buildCertAlipaySafetyRequest() {
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        // 查询配置参数
        String url = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.SAFETY_URL, null);
        if (StrKit.isEmpty(url)) {
            throw new ParamException("没有配置 安全发使用的支付宝网关");
        }
        certAlipayRequest.setServerUrl(url);

        String appId = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.SAFETY_APPID, null);
        if (StrKit.isEmpty(appId)) {
            throw new ParamException("没有配置 安全发使用的APPID");
        }
        certAlipayRequest.setAppId(appId);

        String appPrivateKey = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.SAFETY_APP_PRIVATE_KEY, null);
        if (StrKit.isEmpty(appPrivateKey)) {
            throw new ParamException("没有配置 安全发使用的APP_PRIVATE_KEY");
        }
        certAlipayRequest.setPrivateKey(appPrivateKey);

        String appCertPath = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.SAFETY_APP_CERT_PATH, null);
        if (StrKit.isEmpty(appCertPath)) {
            throw new ParamException("没有配置 安全发使用的APP_CERT_PATH");
        }
        certAlipayRequest.setCertPath(appCertPath);

        String alipayCertPath = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.SAFETY_ALIPAY_CERT_PATH, null);
        if (StrKit.isEmpty(alipayCertPath)) {
            throw new ParamException("没有配置 安全发使用的ALIPAY_CERT_PATH");
        }
        certAlipayRequest.setAlipayPublicCertPath(alipayCertPath);

        String alipayRootCertPath = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.SAFETY_ALIPAY_ROOT_CERT_PATH, null);
        if (StrKit.isEmpty(alipayRootCertPath)) {
            throw new ParamException("没有配置 安全发使用的ALIPAY_ROOT_CERT_PATH");
        }
        certAlipayRequest.setRootCertPath(alipayRootCertPath);
        certAlipayRequest.setFormat("JSON");
        certAlipayRequest.setCharset("UTF-8");
        certAlipayRequest.setSignType("RSA2");
        return certAlipayRequest;
    }

    @Override
    public VxConfigVO selectVxConfigVO(String siteId) {
        VxConfigVO vxConfigVO = new VxConfigVO();
        String spMchid = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.MERCHANT_ID, null);
        if (StrKit.isEmpty(spMchid)) {
            throw new ParamException("没有配置 微信批量转账使用的服务商的商户号");
        }
        vxConfigVO.setSpMchid(spMchid);
        String certificateSerialNo = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.CERTIFICATE_SERIAL_NO, null);
        if (StrKit.isEmpty(certificateSerialNo)) {
            throw new ParamException("没有配置 微信批量转账使用的服务商的证书序列号");
        }
        vxConfigVO.setCertificateSerialNo(certificateSerialNo);
        String privateKeyPath = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.PRIVATE_KEY, null);
        if (StrKit.isEmpty(privateKeyPath)) {
            throw new ParamException("没有配置 微信批量转账使用的服务商的私钥文件的地址");
        }
        vxConfigVO.setPrivateKeyPath(privateKeyPath);
        String wechatpaySerial = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.WECHATPAY_SERIAL, null);
        if (StrKit.isEmpty(wechatpaySerial)) {
            throw new ParamException("没有配置 微信批量转账使用的微信平台证书的序列号");
        }
        vxConfigVO.setWechatpaySerial(wechatpaySerial);
        String vxCertificate = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.VX_CERTIFICATE, null);
        if (StrKit.isEmpty(vxCertificate)) {
            throw new ParamException("没有配置 微信批量转账使用的微信平台的证书路径");
        }
        vxConfigVO.setVxCertificate(vxCertificate);
        // 获取spAppid   租户配置了会查询租户的
        String spAppid = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.SP_APPID, siteId);
        if (StrKit.isEmpty(spAppid)) {
            throw new ParamException("没有配置 微信批量转账使用的appid");
        }
        vxConfigVO.setSpAppid(spAppid);
        // 获取sub_mchid
        String subMchid = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.SUB_MCHID, siteId);
        vxConfigVO.setSubMchid(subMchid);
        // 获取职薪人小程序的appid
        String spAppidXCX = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.SP_APPID_XCX, siteId);
        if (StrKit.isEmpty(spAppidXCX)) {
            throw new ParamException("没有配置 人脸核身使用的小程序的appid");
        }
        vxConfigVO.setSpAppidXCX(spAppidXCX);
        return vxConfigVO;
    }

    /**
     * 修改转账批次审批状态
     *
     * @param transferAccountsBatchId 转账批次id
     * @param userApproId             具体审批id
     * @param tokenUser               当前用户
     */
    private void updateApproveStatus(String transferAccountsBatchId, String userApproId, TokenUser tokenUser) {
        TransferAccountsBatchDO accountsBatchDO = new TransferAccountsBatchDO();
        accountsBatchDO.setId(transferAccountsBatchId);
        // 设置转账批次为审批中
        accountsBatchDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.APPROVE_ING.getValue());
        // 保存审批Id
        accountsBatchDO.setUserApproId(userApproId);
        accountsBatchDO.preUpdate(tokenUser.getUserId());
        // 账单审批状态更新成审批中
        transferAccountsBatchDAO.updateByPrimaryKeySelective(accountsBatchDO);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public String offerAliPay(String requestBodyStr) {
        log.info("批次异步通知参数打印：requestBodyStr：{}", requestBodyStr);

        if (StrKit.isEmpty(requestBodyStr)) {
            log.error("批次异步通知错误，无法修改批次相关属性，requestBodyStr：{}", requestBodyStr);
            return "fail";
        }
        Map<String, String> params = buildParams(requestBodyStr);
        if (!params.containsKey("biz_content")) {
            return "fail";
        }
        BizContent bizContentVO = JSONObject.parseObject(params.get("biz_content"), BizContent.class);
        // 批次id
        String outBatchNo = bizContentVO.getOut_batch_no();
        Example example = new Example(TransferAccountsZfbBatchDO.class);
        example.and()
                .andEqualTo("id", outBatchNo)
                .andEqualTo("valid", true);
        TransferAccountsZfbBatchDO transferAccountsZfbBatchDO = transferAccountsZfbBatchDAO.selectOneByExample(example);
        if (transferAccountsZfbBatchDO == null) {
            return "fail";
        }
        TokenUser tokenUser = new TokenUser("zfb", transferAccountsZfbBatchDO.getSiteId());
        // 修改支付宝的批次信息
        transferAccountsZfbBatchDO.setBatchStatus(bizContentVO.getBatch_status());
        if ("INIT".equals(bizContentVO.getBatch_status())
                || "WAIT_PAY".equals(bizContentVO.getBatch_status())
                || "DEALING".equals(bizContentVO.getBatch_status())) {
            // 批次还没有处理完，还需要发送通知
            return "fail";
        }
        if (StrKit.isNotEmpty(bizContentVO.getSuccess_count())) {
            transferAccountsZfbBatchDO.setSuccessCount(bizContentVO.getSuccess_count());
        }
        if (StrKit.isNotEmpty(bizContentVO.getSuccess_amount())) {
            transferAccountsZfbBatchDO.setSuccessMoney(bizContentVO.getSuccess_amount());
        }
        if (StrKit.isNotEmpty(bizContentVO.getFail_count())) {
            transferAccountsZfbBatchDO.setFailCount(bizContentVO.getFail_count());
        }
        if (StrKit.isNotEmpty(bizContentVO.getFail_amount())) {
            transferAccountsZfbBatchDO.setFailMoney(bizContentVO.getFail_amount());
        }
        transferAccountsZfbBatchService.updateByIdSelective(transferAccountsZfbBatchDO);
        // 调用查询接口  查询明细  修改支付宝批次信息及 hr的批次信息
        selectTransferAccountsBatch(transferAccountsZfbBatchDO.getBatchId(), tokenUser);
        return "success";
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public String offerAliSafetyPay(String requestBodyStr) {
        log.info("安全发批次异步通知参数打印：requestBodyStr：{}", requestBodyStr);
        if (StrKit.isEmpty(requestBodyStr)) {
            log.error("安全发批次异步通知错误，无法修改批次相关属性，requestBodyStr：{}", requestBodyStr);
            return "fail";
        }
        Map<String, String> params = buildParams(requestBodyStr);
        if (!params.containsKey("biz_content")) {
            return "fail";
        }
        BizContentSafety bizContentSafety = JSONObject.parseObject(params.get("biz_content"), BizContentSafety.class);
        if (StrKit.isEmpty(bizContentSafety.getOriginInterface())) {
            return "fail";
        }
        // 根据接口类型加锁
        RLock lock = redissonClient.getLock(bizContentSafety.getOriginInterface());
        boolean hasLock;
        try {
            // 加锁，等待0秒，上锁以后100秒自动解锁
            hasLock = lock.tryLock(0, 100, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if (!hasLock) {
            log.warn("安全发异步通知的订单号：{}", bizContentSafety.getOutBatchNo());
            throw new ParamException("异步通知正在处理");
        }
        try {
            if ("alipay.fund.batch.uni.transfer".equals(bizContentSafety.getOriginInterface())) {
                return safetyBatchAsyn(bizContentSafety);
            } else if ("alipay.fund.trans.uni.transfer".equals(bizContentSafety.getOriginInterface())) {
                BizContentSafetyOne bizContentSafetyOne = JSONObject.parseObject(params.get("biz_content"), BizContentSafetyOne.class);
                return safetyOneToBankCardAsyn(bizContentSafetyOne);
            }
            return "fail";
        } finally {
            //释放锁
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 安全发 批量到户异步通知
     * @param bizContentSafety 异步通知参数
     * @return success or fail
     */
    private String safetyBatchAsyn(BizContentSafety bizContentSafety) {
        // 批次id
        String outBatchNo = bizContentSafety.getOutBatchNo();
        TransferAccountsZfbSafetyBatchDO transferAccountsZfbSafetyBatchDO = transferAccountsZfbSafetyBatchService
                .selectDOById(outBatchNo);
        if (transferAccountsZfbSafetyBatchDO == null) {
            return "fail";
        }
        TokenUser tokenUser = new TokenUser("zfbSafety", transferAccountsZfbSafetyBatchDO.getSiteId());
        // 修改支付宝的批次信息
        transferAccountsZfbSafetyBatchDO.setBatchStatus(bizContentSafety.getBatchStatus());
        if ("INIT".equals(bizContentSafety.getBatchStatus())
                || "WAIT_PAY".equals(bizContentSafety.getBatchStatus())
                || "DEALING".equals(bizContentSafety.getBatchStatus())) {
            // 批次还没有处理完，还需要发送通知
            return "fail";
        }
        if (StrKit.isNotEmpty(bizContentSafety.getSuccessCount())) {
            transferAccountsZfbSafetyBatchDO.setSuccessCount(bizContentSafety.getSuccessCount());
        }
        if (StrKit.isNotEmpty(bizContentSafety.getSuccessAmount())) {
            transferAccountsZfbSafetyBatchDO.setSuccessAmount(bizContentSafety.getSuccessAmount());
        }
        if (StrKit.isNotEmpty(bizContentSafety.getFailCount())) {
            transferAccountsZfbSafetyBatchDO.setFailCount(bizContentSafety.getFailCount());
        }
        if (StrKit.isNotEmpty(bizContentSafety.getFailAmount())) {
            transferAccountsZfbSafetyBatchDO.setFailAmount(bizContentSafety.getFailAmount());
        }
        transferAccountsZfbSafetyBatchService.updateByIdSelective(transferAccountsZfbSafetyBatchDO);
        // 调用查询接口  查询明细  修改支付宝安全发批次信息及 hr的批次信息
        this.selectTransferAccountsBatch(transferAccountsZfbSafetyBatchDO.getBatchId(), tokenUser);
        return "success";
    }

    /**
     * 安全发 单笔到卡异步通知
     * @param bizContentSafetyOne  安全发单笔转账异步通知参数
     * @return success or fail
     */
    private String safetyOneToBankCardAsyn(BizContentSafetyOne bizContentSafetyOne) {
        // 安全发明细批次 id
        String outBizNo = bizContentSafetyOne.getOutBizNo();
        TransferAccountsZfbSafetyDetailDO transferAccountsZfbSafetyDetailDO = transferAccountsZfbSafetyDetailService
                .selectDOByIdNoSiteId(outBizNo);
        if (transferAccountsZfbSafetyDetailDO == null) {
            return "fail";
        }
        TokenUser tokenUser = new TokenUser("zfbSafety", transferAccountsZfbSafetyDetailDO.getSiteId());
        // 修改安全发明细
        transferAccountsZfbSafetyDetailDO.setOrderId(bizContentSafetyOne.getOrderId());
        transferAccountsZfbSafetyDetailDO.setPayFundOrderId(bizContentSafetyOne.getPayFundOrderId());
        transferAccountsZfbSafetyDetailDO.setStatus(bizContentSafetyOne.getStatus());
        transferAccountsZfbSafetyDetailDO.setPayDate(bizContentSafetyOne.getPayDate());
        transferAccountsZfbSafetyDetailDO.setTransAmountAll(bizContentSafetyOne.getTransAmount());
        transferAccountsZfbSafetyDetailDO.setErrorCode(bizContentSafetyOne.getErrorCode());
        transferAccountsZfbSafetyDetailDO.setFailReason(bizContentSafetyOne.getFailReason());
        transferAccountsZfbSafetyDetailService.updateByIdSelective(transferAccountsZfbSafetyDetailDO);

        // 修改hr明细信息
        TransferAccountsDetailDO transferAccountsDetailDO = transferAccountsDetailService
                .selectByThirdPartyId(transferAccountsZfbSafetyDetailDO.getId(), tokenUser.getSiteId());
        ParamException.notNull(transferAccountsDetailDO, "根据第三方批次明细id查询不到hr明细信息，请联系管理员查询原因！");
        String gmtFinish = bizContentSafetyOne.getPayDate();
        if (StrKit.isNotEmpty(gmtFinish)) {
            Date payTime = DateKit8.parse(gmtFinish, DateKit8.DATE_TIME);
            transferAccountsDetailDO.setPayTime(payTime);
        }
        transferAccountsDetailDO.setFailCause(bizContentSafetyOne.getFailReason() == null ? "" : bizContentSafetyOne.getFailReason());
        if ("SUCCESS".equals(bizContentSafetyOne.getStatus())) {
            transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.SUCCESS_PAY.getValue());
        } else if ("FAIL".equals(bizContentSafetyOne.getStatus())) {
            transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
            transferAccountsDetailDO.setIsDispose(false);
        } else if ("DEALING".equals(bizContentSafetyOne.getStatus())) {
            transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.NO_PAY.getValue());
        } else if ("REFUND".equals(bizContentSafetyOne.getStatus())) {
            transferAccountsDetailDO.setIsDispose(false);
            transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
        } else {
            throw new ParamException("支付宝明细信息未知，请稍后查询批次相关信息!");
        }
        transferAccountsDetailDO.setThirdPartyInteriorDetailId(bizContentSafetyOne.getOrderId());
        transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO);

        // 查询批次信息
        TransferAccountsBatchDO transferAccountsBatchDO = this.selectDOById(transferAccountsDetailDO.getBatchId(),
                transferAccountsDetailDO.getSiteId());
        ParamException.notNull(transferAccountsBatchDO, "批次不存在或已删除，请联系管理员查询原因！");

        // 查询该批次的全部批次明细信息
        List<TransferAccountsDetailDO> transferAccountsDetailDOList = transferAccountsDetailService
                .selectByBatchId(transferAccountsDetailDO.getBatchId(), transferAccountsDetailDO.getSiteId());
        // 支付状态分组
        Map<Integer, List<TransferAccountsDetailDO>> transferAccountsDetailDOPayStatusMap = transferAccountsDetailDOList
                .stream().collect(Collectors.groupingBy(TransferAccountsDetailDO::getPayStatus));
        // 存在未支付的明细  直接返回success， 不修改整个批次的信息
        if (transferAccountsDetailDOPayStatusMap.containsKey(TransferAccountsPayStatusEnum.NO_PAY.getValue())) {
            return "success";
        }
        // 没有未支付的订单  说明这是最后一笔明细的异步通知 需要修改整个批次的信息
        List<TransferAccountsDetailDO> transferAccountsDetailDOSuccessList  = transferAccountsDetailDOPayStatusMap
                .get(TransferAccountsPayStatusEnum.SUCCESS_PAY.getValue());
        List<TransferAccountsDetailDO> transferAccountsDetailDOFailList  = transferAccountsDetailDOPayStatusMap
                .get(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
        int successCount = 0;
        int failCount = 0;
        BigDecimal successManey = new BigDecimal("0");
        BigDecimal failManey = new BigDecimal("0");
        if (CollectionKit.isNotEmpty(transferAccountsDetailDOSuccessList)) {
            successCount = transferAccountsDetailDOSuccessList.size();
            for (TransferAccountsDetailDO accountsDetailDO : transferAccountsDetailDOSuccessList) {
                if (accountsDetailDO.getMoney() == null) {
                    throw new ParamException("批次明细id为：" + accountsDetailDO.getId() + "的明细金额为空!");
                }
                successManey = successManey.add(accountsDetailDO.getMoney());
            }
        }
        if (CollectionKit.isNotEmpty(transferAccountsDetailDOFailList)) {
            failCount = transferAccountsDetailDOFailList.size();
            for (TransferAccountsDetailDO accountsDetailDO : transferAccountsDetailDOFailList) {
                if (accountsDetailDO.getMoney() == null) {
                    throw new ParamException("批次明细id为：" + accountsDetailDO.getId() + "的明细金额为空!");
                }
                failManey = failManey.add(accountsDetailDO.getMoney());
            }
        }

        int payStatus;
        if (failCount == 0) {
            payStatus = TransferAccountsPayStatusEnum.SUCCESS_PAY.getValue();
        } else {
            payStatus = TransferAccountsPayStatusEnum.ERROR_PAY.getValue();
            transferAccountsBatchDO.setIsDispose(false);
        }
        transferAccountsBatchDO.setPayStatus(payStatus);
        transferAccountsBatchDO.setSecondType(TransferAccountsSubjectStatusEnum.YET_ISSUE.getValue());
        transferAccountsBatchDO.setSuccessCount(successCount);
        transferAccountsBatchDO.setSuccessMoney(successManey);
        transferAccountsBatchDO.setFailCount(failCount);
        transferAccountsBatchDO.setFailMoney(failManey);
        transferAccountsBatchDO.setTransferCreate(new Date());
        this.updateByIdSelective(transferAccountsBatchDO);
        return "success";
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int selectTransferAccountsBatch(String transferAccountsBatchId, TokenUser tokenUser) {
        // 查询批次
        TransferAccountsBatchDO transferAccountsBatchDO = this
                .selectDOById(transferAccountsBatchId, tokenUser.getSiteId());
        ParamException.notNull(transferAccountsBatchDO, "没有查询到对应的批次信息，请刷新重试!");
        if (!TransferAccountsSubjectStatusEnum.WAIT_ISSUE.getValue().equals(transferAccountsBatchDO.getSubjectStatus())
                || !TransferAccountsPayStatusEnum.PAY_ING.getValue().equals(transferAccountsBatchDO.getPayStatus())) {
            throw new ParamException("批次状态不是发放中，无法查询支付结果!");
        }
        if (TransferAccountsTypeEnum.ZFB.getValue().equals(transferAccountsBatchDO.getType())) {
            if (TransferAccountsSecondTypeEnum.ZFB_BALANCE.getValue().equals(transferAccountsBatchDO.getSecondType())) {
                // 调用支付宝查询
                this.upDateTransferAccountInfoByAliPay(transferAccountsBatchDO, tokenUser);
            } else if (TransferAccountsSecondTypeEnum.ZFB_SAFETY_PAY.getValue().equals(transferAccountsBatchDO.getSecondType())) {
                // 调用支付宝安全发批次查询
                this.upDateTransferAccountInfoByAliPaySafety(transferAccountsBatchDO, tokenUser);
            } else {
                throw new ParamException("批次类型非法，请联系管理员进行查询");
            }
        } else if (TransferAccountsTypeEnum.VX.getValue().equals(transferAccountsBatchDO.getType())) {
            // 调用微信查询
            this.upDateTransferAccountInfoByVxPay(transferAccountsBatchDO, tokenUser);
        } else if (TransferAccountsTypeEnum.BANK.getValue().equals(transferAccountsBatchDO.getType())) {
            // 调用银行查询
            this.upDateTransferAccountInfoByPingAnBank(transferAccountsBatchDO, tokenUser);
        }
        return 1;
    }

    // 查询支付宝转账信息
    private void upDateTransferAccountInfoByAliPay(TransferAccountsBatchDO transferAccountsBatchDO,
                                                   TokenUser tokenUser) {
        // 查询支付宝的批次信息
        TransferAccountsZfbBatchDO transferAccountsZfbBatchDO = transferAccountsZfbBatchService
                .selectDOById(transferAccountsBatchDO.getThirdpartyBatchId(), tokenUser.getSiteId());
        ParamException.notNull(transferAccountsZfbBatchDO, "没有查询到对应的支付宝批次信息，请刷新重试!");
        // 获取appAuthToken
        String appAuthToken = cache.get(CacheKeyKit
                .getSiteAppAuthTokenCacheKey(transferAccountsZfbBatchDO.getPayerUserId()));
        if (StrKit.isEmpty(appAuthToken)) {
            throw new ParamException("商户授权码为空，请引导商户前往授权后重试");
        }
        // 校验appAuthToken
        CertAlipayRequest certAlipayRequest = this.buildCertAlipayRequest();
        if (!Alipay.selectAppAuthTokenStatus(appAuthToken, certAlipayRequest)) {
            throw new ParamException("商户授权token失效，无法查询的订单信息，请引导商户前往授权后重试!");
        }
        // 根据返回结果修改批次信息
        AlipayFundBatchDetailQueryResponse alipayFundBatchDetailQueryResponse =
                Alipay.alipaySelectStatus(transferAccountsZfbBatchDO.getId(), appAuthToken, certAlipayRequest);
        if (alipayFundBatchDetailQueryResponse == null) {
            throw new ParamException("调用支付宝查询接口异常，请联系管理员查询原因！");
        }
        // 修改hr批次信息
        upDateBatchByAliPay(transferAccountsBatchDO, alipayFundBatchDetailQueryResponse);
        // 修改支付宝批次信息
        upDateZfbBatchByAliPay(transferAccountsZfbBatchDO, alipayFundBatchDetailQueryResponse);
        // 修改明细信息(hr的明细 和 支付宝的明细同时修改)
        upDateAllDetailByAliPay(alipayFundBatchDetailQueryResponse, tokenUser);
    }

    /**
     * 根据支付宝查询的结果修改hr批次表的信息
     *
     * @param transferAccountsBatchDO            hr批次信息
     * @param alipayFundBatchDetailQueryResponse 支付宝返回的结果
     */
    private void upDateBatchByAliPay(TransferAccountsBatchDO transferAccountsBatchDO,
                                     AlipayFundBatchDetailQueryResponse alipayFundBatchDetailQueryResponse) {
        transferAccountsBatchDO.setThirdPartyInteriorBatchId(alipayFundBatchDetailQueryResponse.getBatchTransId());
        transferAccountsBatchDO.setFailCause("");
        if ("INIT".equals(alipayFundBatchDetailQueryResponse.getBatchStatus())) {
            // 刚接收批次 直接返回
            throw new ParamException("该订单支付宝还未创建，请稍后查询！");
        } else if ("WAIT_PAY".equals(alipayFundBatchDetailQueryResponse.getBatchStatus())
                || "DEALING".equals(alipayFundBatchDetailQueryResponse.getBatchStatus())) {
            // 批次等待处理
            throw new ParamException("该订单还未支付完成，请稍后查询！");
        } else if ("SUCCESS".equals(alipayFundBatchDetailQueryResponse.getBatchStatus())) {
            transferAccountsBatchDO.setPayStatus(TransferAccountsPayStatusEnum.SUCCESS_PAY.getValue());
        } else if ("PART_SUCCESS".equals(alipayFundBatchDetailQueryResponse.getBatchStatus())) {
            transferAccountsBatchDO.setPayStatus(TransferAccountsPayStatusEnum.ERROR_PAY.getValue());
            transferAccountsBatchDO.setIsDispose(false);
        } else if ("FAIL".equals(alipayFundBatchDetailQueryResponse.getBatchStatus())
                || "DISUSE".equals(alipayFundBatchDetailQueryResponse.getBatchStatus())
                || "INVALID".equals(alipayFundBatchDetailQueryResponse.getBatchStatus())) {
            transferAccountsBatchDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
            // 设置失败原因
            String failCause = "未知原因！";
            switch (alipayFundBatchDetailQueryResponse.getBatchStatus()) {
                case "FAIL":
                    failCause = "全部处理失败";
                    break;
                case "DISUSE":
                    failCause = "批次单据废弃";
                    break;
                case "INVALID":
                    failCause = "批次单据中明细全部无效";
                    break;
                default:
                    break;
            }
            transferAccountsBatchDO.setFailCause(failCause);
        } else {
            log.error("查询支付宝支付结果状态异常，支付宝返回结果：{}", JSON.toJSONString(alipayFundBatchDetailQueryResponse));
            throw new ParamException("状态异常，支付宝返回信息：" + alipayFundBatchDetailQueryResponse.getBatchStatus());
        }
        transferAccountsBatchDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.YET_ISSUE.getValue());
        if (StrKit.isNotEmpty(alipayFundBatchDetailQueryResponse.getFailReason())) {
            // 如果支付宝返回了错误原因，则更新错误原因
            transferAccountsBatchDO.setFailCause(alipayFundBatchDetailQueryResponse.getFailReason());
        }
        if (StrKit.isNotEmpty(alipayFundBatchDetailQueryResponse.getSuccessCount())) {
            transferAccountsBatchDO.setSuccessCount(
                    Integer.parseInt(alipayFundBatchDetailQueryResponse.getSuccessCount() + ""));
        }
        if (StrKit.isNotEmpty(alipayFundBatchDetailQueryResponse.getSuccessAmount())) {
            transferAccountsBatchDO.setSuccessMoney(
                    new BigDecimal(alipayFundBatchDetailQueryResponse.getSuccessAmount()));
        }
        if (StrKit.isNotEmpty(alipayFundBatchDetailQueryResponse.getFailCount())) {
            transferAccountsBatchDO.setFailCount(
                    Integer.parseInt(alipayFundBatchDetailQueryResponse.getFailCount() + ""));
        }
        if (StrKit.isNotEmpty(alipayFundBatchDetailQueryResponse.getFailAmount())) {
            transferAccountsBatchDO.setFailMoney(
                    new BigDecimal(alipayFundBatchDetailQueryResponse.getFailAmount()));
        }
        if (alipayFundBatchDetailQueryResponse.getGmtFinish() != null) {
            transferAccountsBatchDO.setTransferCreate(DateKit8
                    .parse(alipayFundBatchDetailQueryResponse.getGmtFinish() + ":00", DateKit8.DATE_TIME));

        }
        this.updateByIdSelective(transferAccountsBatchDO);
    }

    /**
     * 根据支付宝查询的结果修改支付宝批次表的信息
     *
     * @param transferAccountsZfbBatchDO         支付宝批次信息
     * @param alipayFundBatchDetailQueryResponse 支付宝返回的结果
     */
    private void upDateZfbBatchByAliPay(TransferAccountsZfbBatchDO transferAccountsZfbBatchDO,
                                        AlipayFundBatchDetailQueryResponse alipayFundBatchDetailQueryResponse) {
        transferAccountsZfbBatchDO.setBatchStatus(alipayFundBatchDetailQueryResponse.getBatchStatus());
        if (StrKit.isNotEmpty(alipayFundBatchDetailQueryResponse.getSuccessCount())) {
            transferAccountsZfbBatchDO.setSuccessCount(alipayFundBatchDetailQueryResponse.getSuccessCount() + "");
        }
        if (StrKit.isNotEmpty(alipayFundBatchDetailQueryResponse.getSuccessAmount())) {
            transferAccountsZfbBatchDO.setSuccessMoney(alipayFundBatchDetailQueryResponse.getSuccessAmount());
        }
        if (StrKit.isNotEmpty(alipayFundBatchDetailQueryResponse.getFailCount())) {
            transferAccountsZfbBatchDO.setFailCount(alipayFundBatchDetailQueryResponse.getFailCount() + "");
        }
        if (StrKit.isNotEmpty(alipayFundBatchDetailQueryResponse.getFailAmount())) {
            transferAccountsZfbBatchDO.setFailMoney(alipayFundBatchDetailQueryResponse.getFailAmount());
        }
        transferAccountsZfbBatchDO.setApprovalStatus(alipayFundBatchDetailQueryResponse.getApprovalStatus());
        transferAccountsZfbBatchDO.setErrorCode(alipayFundBatchDetailQueryResponse.getErrorCode());
        transferAccountsZfbBatchDO.setFailReason(alipayFundBatchDetailQueryResponse.getFailReason());
        transferAccountsZfbBatchDO.setGmtFinish(alipayFundBatchDetailQueryResponse.getGmtFinish());
        transferAccountsZfbBatchDO.setGmtPayFinish(alipayFundBatchDetailQueryResponse.getGmtPayFinish());
        transferAccountsZfbBatchDO.setPayerId(alipayFundBatchDetailQueryResponse.getPayerId());
        transferAccountsZfbBatchService.updateByIdSelective(transferAccountsZfbBatchDO);
    }

    /**
     * 根据支付宝查询的结果修改批次明细表的信息
     *
     * @param alipayFundBatchDetailQueryResponse 支付宝返回的结果
     * @param tokenUser                          当前用户
     */
    private void upDateAllDetailByAliPay(AlipayFundBatchDetailQueryResponse alipayFundBatchDetailQueryResponse,
                                         TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(alipayFundBatchDetailQueryResponse.getAccDetailList())) {
            throw new ParamException("批次还未处理，查询明细为空，请稍后查询!");
        }
        List<AccDetailModel> accDetailModelList = alipayFundBatchDetailQueryResponse.getAccDetailList();
        for (AccDetailModel accDetailModel : accDetailModelList) {
            String zfbDetailId = accDetailModel.getOutBizNo();
            if (StrKit.isEmpty(zfbDetailId)) {
                throw new ParamException("支付宝返回的明细id为空，请联系管理员或稍后重新查询!");
            }
            // 查询支付宝的明细信息
            TransferAccountsZfbDetailDO transferAccountsZfbDetailDO =
                    transferAccountsZfbDetailService.selectDOById(zfbDetailId, tokenUser.getSiteId());
            if (Objects.isNull(transferAccountsZfbDetailDO)) {
                log.error("查询批次转账接口，支付宝返回的明细id没有找到：明细id：{}", zfbDetailId);
                throw new ParamException("查询批次订单的支付结果，支付宝返回的信息没有找到，请联系管理员!");
            }
            // 查询hr明细信息
            TransferAccountsDetailDO transferAccountsDetailDO = transferAccountsDetailService
                    .selectByThirdPartyId(transferAccountsZfbDetailDO.getId(), tokenUser.getSiteId());
            ParamException.notNull(transferAccountsDetailDO, "根据第三方批次明细id查询不到hr明细信息，请联系管理员查询原因！");
            // 修改明细信息
            String gmtFinish = accDetailModel.getGmtFinish();
            if (StrKit.isNotEmpty(gmtFinish)) {
                Date payTime = DateKit8.parse(gmtFinish + ":00", DateKit8.DATE_TIME);
                transferAccountsZfbDetailDO.setPayTime(payTime);
                transferAccountsDetailDO.setPayTime(payTime);
            }
            transferAccountsDetailDO.setFailCause(accDetailModel.getErrorMsg() == null ? "" : accDetailModel.getErrorMsg());
            if ("INIT".equals(accDetailModel.getStatus())
                    || "APPLIED".equals(accDetailModel.getStatus())
                    || "DEALED".equals(accDetailModel.getStatus())) {
                transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.NO_PAY.getValue());
            } else if ("SUCCESS".equals(accDetailModel.getStatus())) {
                transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.SUCCESS_PAY.getValue());
            } else if ("DISUSE".equals(accDetailModel.getStatus())) {
                transferAccountsDetailDO.setIsDispose(false);
                transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
            } else if ("FAIL".equals(accDetailModel.getStatus())) {
                transferAccountsDetailDO.setIsDispose(false);
                transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
            } else {
                throw new ParamException("支付宝明细信息未知，请稍后查询批次相关信息!");
            }
            transferAccountsDetailDO.setThirdPartyInteriorDetailId(accDetailModel.getDetailId());
            transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO);
            // 修改支付宝批次明细信息
            transferAccountsZfbDetailDO.setStatus(accDetailModel.getStatus());
            if (accDetailModel.getPayeeInfo() == null) {
                throw new ParamException("支付宝返回的收款方信息为空，请联系管理人员查询原因！");
            }
            transferAccountsZfbDetailDO.setPayeeAccount(accDetailModel.getPayeeInfo().getPayeeAccount());
            transferAccountsZfbDetailDO.setPayeeType(accDetailModel.getPayeeInfo().getPayeeType());
            transferAccountsZfbDetailDO.setPayeeName(accDetailModel.getPayeeInfo().getPayeeName());
            transferAccountsZfbDetailDO.setMoney(accDetailModel.getSettlementAmount() == null
                    ? new BigDecimal("0") : new BigDecimal(accDetailModel.getSettlementAmount()));
            transferAccountsZfbDetailDO.setDetailId(accDetailModel.getDetailId());
            transferAccountsZfbDetailDO.setAlipayOrderNo(accDetailModel.getAlipayOrderNo());
            transferAccountsZfbDetailDO.setErrorCode(accDetailModel.getErrorCode());
            transferAccountsZfbDetailDO.setErrorMsg(accDetailModel.getErrorMsg());
            transferAccountsZfbDetailDO.setZfbGmtCreate(accDetailModel.getGmtCreate());
            transferAccountsZfbDetailDO.setZfbGmtFinish(accDetailModel.getGmtFinish());
            transferAccountsZfbDetailService.updateByIdSelective(transferAccountsZfbDetailDO);
        }
    }

    // 查询支付宝安全发转账信息
    private void upDateTransferAccountInfoByAliPaySafety(TransferAccountsBatchDO transferAccountsBatchDO,
                                                         TokenUser tokenUser) {
        // 查询支付宝安全发的批次信息
        TransferAccountsZfbSafetyBatchDO transferAccountsZfbSafetyBatchDO = transferAccountsZfbSafetyBatchService
                .selectDOById(transferAccountsBatchDO.getThirdpartyBatchId(), tokenUser.getSiteId());
        ParamException.notNull(transferAccountsZfbSafetyBatchDO, "没有查询到对应的支付宝安全发批次信息，请刷新重试!");
        // 查询发放账户
        TransferAccountInfoDO transferAccountInfoDO = transferAccountInfoService.selectByTypeAndAccount(
                TransferAccountsTypeEnum.ZFB_SAFETY.getValue(), transferAccountsZfbSafetyBatchDO.getPayerBookId());
        ParamException.notNull(transferAccountInfoDO, "没有查询到对应的支付宝安全发账户信息，请刷新重试!");

        // 根据返回结果修改批次信息
        CertAlipayRequest certAlipayRequest = this.buildCertAlipaySafetyRequest();
        AlipayUserAgreementQueryResponse alipayUserAgreementQueryResponse = AliSafetyPay
                .selectAgreementNo(certAlipayRequest, transferAccountInfoDO.getZfbLoginId());
        String agreementNo = alipayUserAgreementQueryResponse.getAgreementNo();
        AlipayFundBatchDetailQueryResponse alipayResponse =
                AliSafetyPay.selectPayBatch(certAlipayRequest, transferAccountsZfbSafetyBatchDO.getId(), agreementNo);
        // 修改hr批次信息
        upDateBatchByAliPay(transferAccountsBatchDO, alipayResponse);
        // 修改支付宝批次信息
        upDateZfbBatchByAliPaySafety(transferAccountsZfbSafetyBatchDO, alipayResponse);
        // 修改明细信息(hr的明细 和 支付宝的明细同时修改)
        upDateAllDetailByAliPaySafety(alipayResponse, tokenUser);
    }

    /**
     * 根据支付宝查询的结果修改支付宝安全发批次表的信息
     *
     * @param transferAccountsZfbSafetyBatchDO 支付宝批次信息
     * @param alipayResponse                   支付宝返回的结果
     */
    private void upDateZfbBatchByAliPaySafety(TransferAccountsZfbSafetyBatchDO transferAccountsZfbSafetyBatchDO,
                                              AlipayFundBatchDetailQueryResponse alipayResponse) {
        transferAccountsZfbSafetyBatchDO.setBatchStatus(alipayResponse.getBatchStatus());
        if (StrKit.isNotEmpty(alipayResponse.getSuccessCount())) {
            transferAccountsZfbSafetyBatchDO.setSuccessCount(alipayResponse.getSuccessCount() + "");
        }
        if (StrKit.isNotEmpty(alipayResponse.getSuccessAmount())) {
            transferAccountsZfbSafetyBatchDO.setSuccessAmount(alipayResponse.getSuccessAmount());
        }
        if (StrKit.isNotEmpty(alipayResponse.getFailCount())) {
            transferAccountsZfbSafetyBatchDO.setFailCount(alipayResponse.getFailCount() + "");
        }
        if (StrKit.isNotEmpty(alipayResponse.getFailAmount())) {
            transferAccountsZfbSafetyBatchDO.setFailAmount(alipayResponse.getFailAmount());
        }
        transferAccountsZfbSafetyBatchDO.setProductCode(alipayResponse.getProductCode());
        transferAccountsZfbSafetyBatchDO.setBizScene(alipayResponse.getBizScene());
        transferAccountsZfbSafetyBatchDO.setTotalAmount(alipayResponse.getTotalAmount());
        transferAccountsZfbSafetyBatchDO.setTotalItemCount(alipayResponse.getTotalItemCount() + "");
        transferAccountsZfbSafetyBatchService.updateByIdSelective(transferAccountsZfbSafetyBatchDO);
    }

    /**
     * 根据支付宝查询的结果修改批次明细表的信息
     *
     * @param alipayResponse 支付宝返回的结果
     * @param tokenUser      当前用户
     */
    private void upDateAllDetailByAliPaySafety(AlipayFundBatchDetailQueryResponse alipayResponse,
                                               TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(alipayResponse.getAccDetailList())) {
            throw new ParamException("批次还未处理，查询明细为空，请稍后查询!");
        }
        List<AccDetailModel> accDetailModelList = alipayResponse.getAccDetailList();
        for (AccDetailModel accDetailModel : accDetailModelList) {
            String zfbSafetyDetailId = accDetailModel.getOutBizNo();
            if (StrKit.isEmpty(zfbSafetyDetailId)) {
                throw new ParamException("支付宝返回的明细id为空，请联系管理员或稍后重新查询!");
            }
            // 查询支付宝安全发的明细信息
            TransferAccountsZfbSafetyDetailDO transferAccountsZfbSafetyDetailDO =
                    transferAccountsZfbSafetyDetailService.selectDOById(zfbSafetyDetailId, tokenUser.getSiteId());
            if (Objects.isNull(transferAccountsZfbSafetyDetailDO)) {
                log.error("查询批次转账接口，支付宝返回的明细id没有找到：明细id：{}", zfbSafetyDetailId);
                throw new ParamException("查询安全发批次订单的支付结果，支付宝返回的信息没有找到，请联系管理员!");
            }
            // 查询hr明细信息
            TransferAccountsDetailDO transferAccountsDetailDO = transferAccountsDetailService
                    .selectByThirdPartyId(transferAccountsZfbSafetyDetailDO.getId(), tokenUser.getSiteId());
            ParamException.notNull(transferAccountsDetailDO, "根据第三方批次明细id查询不到hr明细信息，请联系管理员查询原因！");
            // 修改明细信息
            String gmtCreate = accDetailModel.getGmtCreate();
            if (StrKit.isNotEmpty(gmtCreate)) {
                Date payTime = DateKit8.parse(gmtCreate + ":00", DateKit8.DATE_TIME);
                transferAccountsZfbSafetyDetailDO.setPayDate(gmtCreate);
                transferAccountsDetailDO.setPayTime(payTime);
            }
            transferAccountsDetailDO.setFailCause(accDetailModel.getErrorMsg() == null ? "" : accDetailModel.getErrorMsg());
            if ("INIT".equals(accDetailModel.getStatus())
                    || "APPLIED".equals(accDetailModel.getStatus())
                    || "DEALED".equals(accDetailModel.getStatus())) {
                transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.NO_PAY.getValue());
            } else if ("SUCCESS".equals(accDetailModel.getStatus())) {
                transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.SUCCESS_PAY.getValue());
            } else if ("DISUSE".equals(accDetailModel.getStatus())) {
                transferAccountsDetailDO.setIsDispose(false);
                transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
            } else if ("FAIL".equals(accDetailModel.getStatus())) {
                transferAccountsDetailDO.setIsDispose(false);
                transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
            } else {
                throw new ParamException("支付宝明细信息未知，请稍后查询批次相关信息!");
            }
            transferAccountsDetailDO.setThirdPartyInteriorDetailId(accDetailModel.getDetailId());
            transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO);

            // 修改支付宝安全发批次明细信息
            transferAccountsZfbSafetyDetailDO.setStatus(accDetailModel.getStatus());
            if (accDetailModel.getPayeeInfo() == null) {
                throw new ParamException("支付宝返回的收款方信息为空，请联系管理人员查询原因！");
            }
            transferAccountsZfbSafetyDetailDO.setIdentity(accDetailModel.getPayeeInfo().getPayeeAccount());
            transferAccountsZfbSafetyDetailDO.setIdentityType(accDetailModel.getPayeeInfo().getPayeeType());
            transferAccountsZfbSafetyDetailDO.setName(accDetailModel.getPayeeInfo().getPayeeName());
            transferAccountsZfbSafetyDetailDO.setOrderId(accDetailModel.getDetailId());
            transferAccountsZfbSafetyDetailDO.setStatus(accDetailModel.getStatus());
            transferAccountsZfbSafetyDetailDO.setRemark(accDetailModel.getRemark());
            transferAccountsZfbSafetyDetailDO.setErrorCode(accDetailModel.getErrorCode());
            transferAccountsZfbSafetyDetailDO.setFailReason(accDetailModel.getErrorMsg());
            transferAccountsZfbSafetyDetailService.updateByIdSelective(transferAccountsZfbSafetyDetailDO);
        }
    }


    // 查询微信转账信息
    private void upDateTransferAccountInfoByVxPay(TransferAccountsBatchDO transferAccountsBatchDO, TokenUser tokenUser) {
        // 查询hr的批次明细信息
        List<TransferAccountsDetailDO> transferAccountsDetailDOList = transferAccountsDetailService
                .selectByBatchId(transferAccountsBatchDO.getId(), tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(transferAccountsDetailDOList)) {
            throw new ParamException("该批次对应的明细全部被删除，无法查询明细信息");
        }
        // 获取微信服务商全部的配置信息
        VxConfigVO vxConfigVO = this.selectVxConfigVO(tokenUser.getSiteId());
        // 查询微信的批次信息
        TransferAccountsVxBatchDO transferAccountsVxBatchDO = transferAccountsVxBatchService
                .selectDOById(transferAccountsBatchDO.getThirdpartyBatchId(), tokenUser.getSiteId());
        ParamException.notNull(transferAccountsVxBatchDO, "没有查询到对应的微信批次信息，请刷新重试!");
        // 拼接get的URL
        String url = "https://api.mch.weixin.qq.com/v3/partner-transfer/batches/out-batch-no/" +
                transferAccountsVxBatchDO.getId() +
                "?need_query_detail=true&limit=100&detail_status=FAIL";
        // 获取微信请求头的Authorization
        String authorization = VXpayClear.generateAuthorization("", "GET", url,
                vxConfigVO.getSpMchid(), vxConfigVO.getCertificateSerialNo(), vxConfigVO.getPrivateKeyPath());
        // 查询微信批次信息
        String vxReturnStr = VXpayClear.vxSelectTransferAccountsInfo(url, authorization, vxConfigVO.getWechatpaySerial());
        VxReturnBatchVO vxReturnBatchVO = JSON.parseObject(vxReturnStr, VxReturnBatchVO.class);
        if (!transferAccountsVxBatchDO.getId().equals(vxReturnBatchVO.getOutBatchNo())) {
            throw new ParamException("查询到的返回信息和系统发起的信息id不匹配，请联系管理员查询具体原因！");
        }
        // 修改hr批次信息
        upDateBatchByVxPay(transferAccountsBatchDO, vxReturnBatchVO);
        // 修改微信批次信息
        upDateVxBatchByVxPay(transferAccountsVxBatchDO, vxReturnBatchVO);
        // 修改明细信息(hr的明细 和 微信的明细同时修改)
        upDateAllDetailByVxPay(transferAccountsVxBatchDO.getId(), transferAccountsDetailDOList, tokenUser,
                vxConfigVO.getSpMchid(), vxConfigVO.getCertificateSerialNo(),
                vxConfigVO.getPrivateKeyPath(), vxConfigVO.getWechatpaySerial(),
                vxReturnBatchVO);
    }

    /**
     * 根据微信查询的结果修改hr批次表的信息
     *
     * @param transferAccountsBatchDO hr批次信息
     * @param vxReturnBatchVO         微信返回的批次查询结果
     */
    private void upDateBatchByVxPay(TransferAccountsBatchDO transferAccountsBatchDO,
                                    VxReturnBatchVO vxReturnBatchVO) {
        transferAccountsBatchDO.setThirdPartyInteriorBatchId(vxReturnBatchVO.getBatchId());
        if ("WAIT_PAY".equals(vxReturnBatchVO.getBatchStatus())) {
            throw new ParamException("该订单正在等待支付，请稍后查询！");
        } else if ("ACCEPTED".equals(vxReturnBatchVO.getBatchStatus())
                || "PROCESSING".equals(vxReturnBatchVO.getBatchStatus())) {
            // 批次等待处理
            throw new ParamException("该订单还未支付完成，请稍后查询！");
        } else if ("FINISHED".equals(vxReturnBatchVO.getBatchStatus())) {
            int payStatus;
            // 完成状态查询失败笔数，如果失败笔数>0 则表示批次异常
            if ((vxReturnBatchVO.getFailNum() != null && vxReturnBatchVO.getFailNum() > 0)) {
                payStatus = TransferAccountsPayStatusEnum.ERROR_PAY.getValue();
                transferAccountsBatchDO.setIsDispose(false);
            } else {
                payStatus = TransferAccountsPayStatusEnum.SUCCESS_PAY.getValue();
            }
            transferAccountsBatchDO.setPayStatus(payStatus);
            transferAccountsBatchDO.setFailCause("");
        } else if ("CLOSED".equals(vxReturnBatchVO.getBatchStatus())) {
            transferAccountsBatchDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
            String failCause = "未知原因";
            if ("MERCHANT_REVOCATION".equals(vxReturnBatchVO.getCloseReason())) {
                failCause = "商户主动撤销";
            } else if ("OVERDUE_CLOSE".equals(vxReturnBatchVO.getCloseReason())) {
                failCause = "系统超时关闭";
            }
            transferAccountsBatchDO.setFailCause(failCause);
        } else {
            log.error("查询微信支付结果状态异常，微信返回结果：{}", JSON.toJSONString(vxReturnBatchVO));
            throw new ParamException("状态异常，微信返回批次状态信息：" + vxReturnBatchVO.getBatchStatus());
        }
        transferAccountsBatchDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.YET_ISSUE.getValue());
        transferAccountsBatchDO.setSuccessCount(vxReturnBatchVO.getSuccessNum());
        if (vxReturnBatchVO.getSuccessAmount() != null) {
            BigDecimal successMoney = new BigDecimal(vxReturnBatchVO.getSuccessAmount())
                    .divide(new BigDecimal("100"), 2, BigDecimal.ROUND_DOWN);
            transferAccountsBatchDO.setSuccessMoney(successMoney);
        }
        transferAccountsBatchDO.setFailCount(vxReturnBatchVO.getFailNum());
        if (vxReturnBatchVO.getFailAmount() != null) {
            BigDecimal failMoney = new BigDecimal(vxReturnBatchVO.getFailAmount())
                    .divide(new BigDecimal("100"), 2, BigDecimal.ROUND_DOWN);
            transferAccountsBatchDO.setFailMoney(failMoney);
        }
        if (StrKit.isNotEmpty(vxReturnBatchVO.getUpdateTime())) {
            transferAccountsBatchDO.setTransferCreate(new DateTime(vxReturnBatchVO.getUpdateTime()).toDate());
        }
        this.updateByIdSelective(transferAccountsBatchDO);
    }

    /**
     * 根据微信查询的结果修改微信批次表的信息
     *
     * @param transferAccountsVxBatchDO 微信批次信息
     * @param vxReturnBatchVO           微信返回批次查询的结果
     */
    private void upDateVxBatchByVxPay(TransferAccountsVxBatchDO transferAccountsVxBatchDO,
                                      VxReturnBatchVO vxReturnBatchVO) {
        transferAccountsVxBatchDO.setBatchStatus(vxReturnBatchVO.getBatchStatus());
        transferAccountsVxBatchDO.setBatchType(vxReturnBatchVO.getBatchType());
        transferAccountsVxBatchDO.setBatchName(vxReturnBatchVO.getBatchName());
        transferAccountsVxBatchDO.setBatchRemark(vxReturnBatchVO.getBatchRemark());
        transferAccountsVxBatchDO.setCloseReason(vxReturnBatchVO.getCloseReason());
        transferAccountsVxBatchDO.setTotalAmount(vxReturnBatchVO.getTotalAmount());
        transferAccountsVxBatchDO.setTotalNum(vxReturnBatchVO.getTotalNum());
        transferAccountsVxBatchDO.setCreateTime(vxReturnBatchVO.getCreateTime());
        transferAccountsVxBatchDO.setUpdateTime(vxReturnBatchVO.getUpdateTime());
        transferAccountsVxBatchDO.setSuccessAmount(vxReturnBatchVO.getSuccessAmount());
        transferAccountsVxBatchDO.setSuccessNum(vxReturnBatchVO.getSuccessNum());
        transferAccountsVxBatchDO.setTransferPurpose(vxReturnBatchVO.getTransferPurpose());
        transferAccountsVxBatchService.updateByIdSelective(transferAccountsVxBatchDO);
    }

    /**
     * 查询微信的明细结果修改 批次明细表的信息（hr批次明细 和 微信批次明细一起修改）
     *
     * @param transferAccountsVxBatchId    微信批次表id（微信批次下单时的商户唯一标识）
     * @param transferAccountsDetailDOList hr批次明细List
     * @param tokenUser                    当前用户
     */
    private void upDateAllDetailByVxPay(String transferAccountsVxBatchId,
                                        List<TransferAccountsDetailDO> transferAccountsDetailDOList,
                                        TokenUser tokenUser, String merchantId, String certificateSerialNo,
                                        String privateKeyPath, String wechatpaySerial,
                                        VxReturnBatchVO vxReturnBatchVO) {
        String batchStatusVx = vxReturnBatchVO.getBatchStatus();
        if ("CLOSED".equals(batchStatusVx)) {
            // 已关闭订单没有明细可以查询
            for (TransferAccountsDetailDO transferAccountsDetailDO : transferAccountsDetailDOList) {
                // 修改hr批次明细信息
                transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
                // 设置失败原因
                String failCause = "批次关闭！";
                transferAccountsDetailDO.setFailCause(failCause);
                transferAccountsDetailDO.setIsDispose(false);
                transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO);
            }
            return;
        }
        for (TransferAccountsDetailDO transferAccountsDetailDO : transferAccountsDetailDOList) {
            // 拼接get的URL
            String url = "https://api.mch.weixin.qq.com/v3/partner-transfer/batches/out-batch-no/" +
                    transferAccountsVxBatchId +
                    "/details/out-detail-no/" +
                    transferAccountsDetailDO.getThirdpartyDetailId();
            // 获取微信请求头的Authorization
            String authorization = VXpayClear.generateAuthorization("", "GET", url,
                    merchantId, certificateSerialNo, privateKeyPath);
            // 查询微信批次明细信息
            String vxReturnStr = VXpayClear.vxSelectTransferAccountsInfo(url, authorization, wechatpaySerial);
            VxRetuenDetailVO vxRetuenDetailVO = JSON.parseObject(vxReturnStr, VxRetuenDetailVO.class);
            if (!transferAccountsVxBatchId.equals(vxRetuenDetailVO.getOutBatchNo())) {
                throw new ParamException("查询到的返回信息和系统发起的信息id不匹配，请联系管理员查询具体原因！");
            }
            // 修改hr批次明细信息
            transferAccountsDetailDO.setThirdPartyInteriorDetailId(vxRetuenDetailVO.getDetailId());
            if ("PROCESSING".equals(vxRetuenDetailVO.getDetailStatus())) {
                throw new ParamException("批次明细还在处理中，请稍后查询批次转账明细");
            } else if ("SUCCESS".equals(vxRetuenDetailVO.getDetailStatus())) {
                transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.SUCCESS_PAY.getValue());
                transferAccountsDetailDO.setFailCause(null);
                transferAccountsDetailDO.setIsDispose(null);
            } else {
                transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
                // 设置失败原因
                String failCause = "未知原因！";
                if (StrKit.isNotEmpty(vxRetuenDetailVO.getFailReason())) {
                    switch (vxRetuenDetailVO.getFailReason()) {
                        case "ACCOUNT_FROZEN":
                            failCause = "账户冻结";
                            break;
                        case "REAL_NAME_CHECK_FAIL":
                            failCause = "用户未实名";
                            break;
                        case "NAME_NOT_CORRECT":
                            failCause = "用户姓名校验失败";
                            break;
                        case "OPENID_INVALID":
                            failCause = "账号校验失败";
                            break;
                        case "TRANSFER_QUOTA_EXCEED":
                            failCause = "超过用户单笔收款额度";
                            break;
                        case "DAY_RECEIVED_QUOTA_EXCEED":
                            failCause = "超过用户单日收款额度";
                            break;
                        case "MONTH_RECEIVED_QUOTA_EXCEED":
                            failCause = "超过用户单月收款额度";
                            break;
                        case "DAY_RECEIVED_COUNT_EXCEED":
                            failCause = "超过用户单日收款次数";
                            break;
                        case "PRODUCT_AUTH_CHECK_FAIL":
                            failCause = "产品权限校验失败";
                            break;
                        case "OVERDUE_CLOSE":
                            failCause = "转账关闭";
                            break;
                        case "ID_CARD_NOT_CORRECT":
                            failCause = "用户身份证校验失败";
                            break;
                        case "ACCOUNT_NOT_EXIST":
                            failCause = "用户账户不存在";
                            break;
                        case "TRANSFER_RISK":
                            failCause = "转账存在风险";
                            break;
                        case "PAYROLL_CARD_ALREADY_LOGOUT":
                            failCause = "该用户的微工卡已经注销";
                            break;
                        case "PAYROLL_CARD_ALREADY_FROZEN":
                            failCause = "该用户的微工卡已经被冻结";
                            break;
                        case "PAYROLL_CARD_UNAUTHORIZED":
                            failCause = "该用户的微工卡未授权该商户";
                            break;
                        case "PAYROLL_CARD_USER_NOT_OPEN":
                            failCause = "该用户没有开通微工卡";
                            break;
                        default:
                            break;
                    }
                }
                transferAccountsDetailDO.setFailCause(failCause);
                transferAccountsDetailDO.setIsDispose(false);
            }
            if (StrKit.isNotEmpty(vxRetuenDetailVO.getUpdateTime())) {
                transferAccountsDetailDO.setPayTime(new DateTime(vxRetuenDetailVO.getUpdateTime()).toDate());
            }
            transferAccountsDetailService.updateById(transferAccountsDetailDO);
            // 修改微信的批次信息
            TransferAccountsVxDetailDO transferAccountsVxDetailDO = transferAccountsVxDetailService
                    .selectDOById(transferAccountsDetailDO.getThirdpartyDetailId(), tokenUser.getSiteId());
            ParamException.notNull(transferAccountsDetailDO, "微信批次明细没有找到，请联系管理员查询原因！");
            transferAccountsVxDetailDO.setDetailStatus(vxRetuenDetailVO.getDetailStatus());
            transferAccountsVxDetailDO.setTransferAmount(vxRetuenDetailVO.getTransferAmount());
            transferAccountsVxDetailDO.setTransferRemark(vxRetuenDetailVO.getTransferRemark());
            transferAccountsVxDetailDO.setFailReason(vxRetuenDetailVO.getFailReason());
            transferAccountsVxDetailDO.setOpenid(vxRetuenDetailVO.getOpenid());
            transferAccountsVxDetailDO.setDetailId(vxRetuenDetailVO.getDetailId());
            String userName = vxRetuenDetailVO.getUsername();
            // 解密姓名
//            userName = VXpayClear.rsaDecryptOAEP(userName, privateKeyPath);
            transferAccountsVxDetailDO.setUsername(userName);
            transferAccountsVxDetailDO.setInitiateTime(vxRetuenDetailVO.getInitiateTime());
            transferAccountsVxDetailDO.setUpdateTime(vxRetuenDetailVO.getUpdateTime());
            transferAccountsVxDetailService.updateById(transferAccountsVxDetailDO);
        }
    }

    // 查询平安银行转账信息
    private void upDateTransferAccountInfoByPingAnBank(TransferAccountsBatchDO transferAccountsBatchDO, TokenUser tokenUser) {
        try {
            // 查询平安银行批次信息
            TransferAccountsBankBatchVO transferAccountsBankBatchVO = transferAccountsBankBatchService
                    .selectById(transferAccountsBatchDO.getThirdpartyBatchId(), tokenUser.getSiteId());
            ParamException.notNull(transferAccountsBankBatchVO, "没有查询到对应的平安银行批次信息，请刷新重试!");
            // 查询发放账户信息
            TransferAccountInfoDO transferAccountInfoDO = transferAccountInfoService
                    .selectByTypeAndAccount(TransferAccountsTypeEnum.BANK.getValue(), transferAccountsBankBatchVO.getSrcAccNo());
            ParamException.notNull(transferAccountInfoDO, "发放账户不存在或已删除，请联系管理员！");
            // 查询hr的批次明细信息
            List<TransferAccountsDetailDO> transferAccountsDetailDOList = transferAccountsDetailService
                    .selectByBatchId(transferAccountsBatchDO.getId(), tokenUser.getSiteId());
            if (CollectionUtils.isEmpty(transferAccountsDetailDOList)) {
                throw new ParamException("该批次对应的明细全部被删除，无法查询明细信息");
            }
            // 构造请求报文头
            String xmlStr = "<?xml version=\"1.0\" encoding=\"GBK\"?>" +
                    "<Result>" +
                    "<ThirdVoucher>" + transferAccountsBankBatchVO.getThirdVoucher() + "</ThirdVoucher>" +
                    "</Result>";
            HrOneKeyPayProperties.PingAn pingAn = hrOneKeyPayProperties.getPingAn();
            String messageLength = String.format("%010d", xmlStr.getBytes("GBK").length);
            String dealCode = "4048  ";
            String serveType = "01";
            Date date = new Date();
            String dealDate = DateKit8.format(date, "yyyyMMdd");
            String dealTime = DateKit8.format(date, "HHmmss");
            String requestHeader = pingAn.getMessageType() + pingAn.getTargetSystemNumber() +
                    pingAn.getMessageEncoder() + pingAn.getMessageAgreement() +
                    transferAccountInfoDO.getCompanyCode() + messageLength + dealCode + pingAn.getOperatorCode() +
                    serveType + dealDate + dealTime + transferAccountsBankBatchVO.getThirdVoucher() +
                    "0000000000000000000000000000000000000000000000000000000000000000000000000000000" +
                    "00000000000000000000000000000000000000000000000000000000";
            String returnStr = PingAnBankPay.tcpPost("127.0.0.1", 7073, requestHeader + xmlStr);
            log.info("批次id:{},===查询平安银行批次转账返回结果:{},===入参:{}",
                    transferAccountsBatchDO.getId(), returnStr, requestHeader + xmlStr);
            if (!returnStr.contains("<?xml")) {
                if (returnStr.contains("YQ9996") || returnStr.contains("BIB010")) {
                    throw new ParamException("查询批次不存在，请联系管理员查询原因!");
                }
                if (returnStr.contains("BIB008")) {
                    throw new ParamException("批次处理中，请稍后再查");
                }
                if (returnStr.contains("BIB009")) {
                    // 批次整体失败时：设置批次及批次明细状态为失败
                    setAllBankStatusFail(transferAccountsBatchDO, transferAccountsDetailDOList, tokenUser);
                    return;
                }
                throw new ParamException("查询失败！银行返回信息无法解析，请联系管理员查询原因");
            }
            returnStr = returnStr.substring(returnStr.indexOf("<?xml"));
            PingAnBankSelectBatchVO pingAnBankSelectBatchVO = PingAnBankPay.converyToPingAnBankSelectBatchVO(returnStr,
                    PingAnBankSelectBatchVO.class);
            // 修改批次信及明细信息
            upDateAllBatchByPingAnBankSelect(transferAccountsBatchDO, pingAnBankSelectBatchVO, tokenUser);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ParamException(e.getMessage());
        }
    }

    // 设置银行的批次及明细全部失败
    private void setAllBankStatusFail(TransferAccountsBatchDO transferAccountsBatchDO,
                                      List<TransferAccountsDetailDO> transferAccountsDetailDOList,
                                      TokenUser tokenUser) {
        transferAccountsBatchDO.setFailCause("银行处理失败！具体原因请联系管理员");
        transferAccountsBatchDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
        transferAccountsBatchDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.YET_ISSUE.getValue());
        transferAccountsBatchDO.setSuccessCount(0);
        transferAccountsBatchDO.setSuccessMoney(new BigDecimal("0"));
        transferAccountsBatchDO.setFailCount(transferAccountsBatchDO.getTotalCount());
        transferAccountsBatchDO.setFailMoney(transferAccountsBatchDO.getMoney());
        this.updateByIdSelective(transferAccountsBatchDO, tokenUser.getSiteId(), tokenUser.getUserId());
        for (TransferAccountsDetailDO transferAccountsDetailDO : transferAccountsDetailDOList) {
            // 修改hr批次明细信息
            transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
            // 设置失败原因
            String failCause = "银行处理失败！！";
            transferAccountsDetailDO.setFailCause(failCause);
            transferAccountsDetailDO.setIsDispose(false);
            transferAccountsDetailService.updateByIdSelective(transferAccountsDetailDO, tokenUser.getSiteId(), tokenUser.getUserId());
        }
    }

    /**
     * 根据平安银行查询的结果修改批次表的信息
     *
     * @param transferAccountsBatchDO 微信批次信息
     * @param pingAnBankSelectBatchVO 查询到的平安银行批次信息
     * @param tokenUser 当前用户
     */
    private void upDateAllBatchByPingAnBankSelect(TransferAccountsBatchDO transferAccountsBatchDO,
                                                  PingAnBankSelectBatchVO pingAnBankSelectBatchVO,
                                                  TokenUser tokenUser) {
        int successCount = 0;
        BigDecimal successMoney = new BigDecimal("0");
        int failCount = 0;
        BigDecimal failMoney = new BigDecimal("0");
        String thirdVoucher = pingAnBankSelectBatchVO.getThirdVoucher();
        if (pingAnBankSelectBatchVO.getList() != null) {
            for (PingAnBankSelectDetailVO pingAnBankSelectDetailVO : pingAnBankSelectBatchVO.getList()) {
                String sThirdVoucher = pingAnBankSelectDetailVO.getSThirdVoucher();
                // 查询平安银行批次明细信息
                TransferAccountsBankDetailDO transferAccountsBankDetailDO = transferAccountsBankDetailService
                        .selectDOBySThirdVoucher(sThirdVoucher, tokenUser.getSiteId());
                if (transferAccountsBankDetailDO == null) {
                    log.error("平安银行返回明细流水号在hr系统中不存在:{}", JSONObject.toJSONString(pingAnBankSelectDetailVO));
                    return;
                }
                transferAccountsBankDetailDO.setIdType(pingAnBankSelectDetailVO.getIdType());
                transferAccountsBankDetailDO.setIdNo(pingAnBankSelectDetailVO.getIdNo());
                transferAccountsBankDetailDO.setAmount(pingAnBankSelectDetailVO.getAmount());
                transferAccountsBankDetailDO.setPostScript(pingAnBankSelectDetailVO.getPostScript());
                transferAccountsBankDetailDO.setFee(pingAnBankSelectDetailVO.getFee());
                transferAccountsBankDetailDO.setStt(pingAnBankSelectDetailVO.getStt());
                transferAccountsBankDetailDO.setSttInfo(pingAnBankSelectDetailVO.getSttInfo());
                transferAccountsBankDetailDO.setRemarkFcr(pingAnBankSelectDetailVO.getRemarkFCR());
                transferAccountsBankDetailService.updateByIdSelective(transferAccountsBankDetailDO,
                        tokenUser.getSiteId(), tokenUser.getUserId());
                // 查询hr批次明细信息
                TransferAccountsDetailDO transferAccountsDetailDO = transferAccountsDetailService
                        .selectByThirdPartyId(transferAccountsBankDetailDO.getId(), tokenUser.getSiteId());
                if (transferAccountsDetailDO == null) {
                    log.error("平安银行返回明细id在hr明细中不存在:{}", JSONObject.toJSONString(transferAccountsBankDetailDO));
                    return;
                }
                // 修改hr批次明细信息
                transferAccountsDetailDO.setThirdPartyInteriorDetailId(sThirdVoucher);
                if ("0000".equals(pingAnBankSelectDetailVO.getStt())) {
                    transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.SUCCESS_PAY.getValue());
                    transferAccountsDetailDO.setFailCause(null);
                    transferAccountsDetailDO.setIsDispose(null);
                    successCount += 1;
                    successMoney = successMoney.add(new BigDecimal(pingAnBankSelectDetailVO.getAmount()));
                } else {
                    transferAccountsDetailDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
                    // 设置失败原因
                    transferAccountsDetailDO.setFailCause(pingAnBankSelectDetailVO.getSttInfo());
                    transferAccountsDetailDO.setIsDispose(false);
                    failCount += 1;
                    failMoney = failMoney.add(new BigDecimal(pingAnBankSelectDetailVO.getAmount()));
                }
                transferAccountsDetailDO.setPayTime(new Date());
                // 添加手续费到hr明细中
                BigDecimal fee;
                if (StrKit.isEmpty(pingAnBankSelectDetailVO.getFee())) {
                    fee = new BigDecimal("0");
                } else {
                    fee = new BigDecimal(pingAnBankSelectDetailVO.getFee());
                }
                transferAccountsDetailDO.setFee(fee);
                transferAccountsDetailService.updateById(transferAccountsDetailDO);
            }
        }

        // 查询平安银行批次信息
        TransferAccountsBankBatchDO transferAccountsBankBatchDO = transferAccountsBankBatchService
                .selectDOByThirdVoucher(thirdVoucher, tokenUser.getSiteId());
        if (transferAccountsBankBatchDO == null) {
            log.error("平安银行返回批次凭证号在hr系统中不存在:{}", JSONObject.toJSONString(pingAnBankSelectBatchVO));
            return;
        }
        transferAccountsBankBatchDO.setBstt(pingAnBankSelectBatchVO.getBStt());
        transferAccountsBankBatchDO.setHostFlowNo(pingAnBankSelectBatchVO.getHostFlowNo());
        transferAccountsBankBatchDO.setHostBusinessNo(pingAnBankSelectBatchVO.getHostBusinessNo());
        transferAccountsBankBatchService.updateByIdSelective(transferAccountsBankBatchDO, tokenUser.getSiteId(), tokenUser.getUserId());

        // 修改hr批次信息
        transferAccountsBatchDO.setThirdPartyInteriorBatchId(thirdVoucher);
        transferAccountsBatchDO.setSubjectStatus(TransferAccountsSubjectStatusEnum.YET_ISSUE.getValue());
        if ("4".equals(pingAnBankSelectBatchVO.getBStt())) {
            Integer payStatus;
            if (failCount == 0) {
                payStatus = TransferAccountsPayStatusEnum.SUCCESS_PAY.getValue();
            } else {
                payStatus = TransferAccountsPayStatusEnum.ERROR_PAY.getValue();
                transferAccountsBatchDO.setIsDispose(false);
            }
            transferAccountsBatchDO.setPayStatus(payStatus);
            transferAccountsBatchDO.setSuccessCount(successCount);
            transferAccountsBatchDO.setSuccessMoney(successMoney);
            transferAccountsBatchDO.setFailCount(failCount);
            transferAccountsBatchDO.setFailMoney(failMoney);
        } else {
            transferAccountsBatchDO.setPayStatus(TransferAccountsPayStatusEnum.FAIL_PAY.getValue());
            transferAccountsBatchDO.setSuccessCount(0);
            transferAccountsBatchDO.setSuccessMoney(new BigDecimal("0"));
            transferAccountsBatchDO.setFailCount(transferAccountsBatchDO.getTotalCount());
            transferAccountsBatchDO.setFailMoney(transferAccountsBatchDO.getMoney());
            transferAccountsBatchDO.setFailCause("银行处理失败，如需查询具体原因请联系管理员！");
        }
        this.updateByIdSelective(transferAccountsBatchDO);
    }

    @Override
    public String setAppAuthTokenToRedis(String appAuthCode) {
        CertAlipayRequest certAlipayRequest = this.buildCertAlipayRequest();
        if (StrKit.isEmpty(appAuthCode)) {
            throw new ParamException("appAuthCode不能为空");
        }
        // 根据code生成appAuthToken 放入缓存中
        StringBuilder pid = new StringBuilder();
        String appAuthToken = Alipay.alipayOpenAuthTokenApp(appAuthCode, certAlipayRequest, pid);
        ParamException.notNull(appAuthToken, "根据code生成appAuthToken失败，请联系管理员查看失败原因!");
        cache.delKey(CacheKeyKit.getSiteAppAuthTokenCacheKey(pid.toString()));
        cache.set(CacheKeyKit.getSiteAppAuthTokenCacheKey(pid.toString()), appAuthToken);
        return appAuthToken;
    }

    @Override
    public TransferAccountsBatchStatisticsVO statisticsBatchStatusByCompanyIds(List<String> companyIds, TokenUser tokenUser) {
        // 查询状态是 待申请的批次 个数
        int awaitApply = this.selectCountByStatus(tokenUser.getSiteId(), TransferAccountsSubjectStatusEnum.WAIT_APPLICATION.getValue(),
                null, companyIds);

        // 查询状态是 审批中的批次 个数
        int approveIng = this.selectCountByStatus(tokenUser.getSiteId(), TransferAccountsSubjectStatusEnum.APPROVE_ING.getValue(),
                null, companyIds);

        // 查询状态是 已驳回的批次 个数
        int reject = this.selectCountByStatus(tokenUser.getSiteId(), TransferAccountsSubjectStatusEnum.APPROVE_REJECT.getValue(),
                null, companyIds);

        // 查询状态是 待发放的批次 个数
        int awaitIssue = this.selectCountByStatus(tokenUser.getSiteId(), TransferAccountsSubjectStatusEnum.WAIT_ISSUE.getValue(),
                null, companyIds);

        // 查询状态是 已发放  支付状态是 支付成功 的批次 个数
        int issueSuccess = this.selectCountByStatus(tokenUser.getSiteId(), TransferAccountsSubjectStatusEnum.YET_ISSUE.getValue(),
                TransferAccountsPayStatusEnum.SUCCESS_PAY.getValue(), companyIds);

        // 查询状态是 已发放  支付状态是 支付失败 的批次 个数
        int issueFail = this.selectCountByStatus(tokenUser.getSiteId(), TransferAccountsSubjectStatusEnum.YET_ISSUE.getValue(),
                TransferAccountsPayStatusEnum.FAIL_PAY.getValue(), companyIds);

        // 查询状态是 已发放  支付状态是 支付异常 的批次 个数
        int issueError = this.selectCountByStatus(tokenUser.getSiteId(), TransferAccountsSubjectStatusEnum.YET_ISSUE.getValue(),
                TransferAccountsPayStatusEnum.ERROR_PAY.getValue(), companyIds);
        return new TransferAccountsBatchStatisticsVO(awaitApply, approveIng, reject, awaitIssue,
                issueSuccess, issueFail, issueError);
    }


    private int selectCountByStatus(String siteId, Integer subjectStatus, Integer payStatus, List<String> companyIds) {
        Example example = new Example(TransferAccountsBatchDO.class);
        example.and()
                .andEqualTo("siteId", siteId)
                .andEqualTo("subjectStatus", subjectStatus)
                .andEqualTo("valid", true);
        if (payStatus != null) {
            example.and().andEqualTo("payStatus", payStatus);
        }
        if (CollectionKit.isNotEmpty(companyIds)) {
            example.and()
                    .andIn("companyId", companyIds);
        }
        return transferAccountsBatchDAO.selectCountByExample(example);
    }

    @Override
    public Page<TransferAccountsBatchListVO> pageApprove(TransferAccountsBatchQuery batchQuery, TokenUser tokenUser) {
        // 审批状态：1：审批中 2：已通过 3：已驳回 4：抄送我
        Integer approveStatus = batchQuery.getApproveStatus();
        Page<TransferAccountsBatchListVO> page = new Page<>();
        // 查询某种审批类型的数据并设置审批状态
        if (approveStatus != null) {
            switch (approveStatus) {
                case APPROVING:
                    // 查询审批中
                    page = pageApproving(batchQuery, tokenUser);
                    getApproPage(page, APPROVING);
                    break;
                case APPROVED_PASS:
                    // 查询已通过
                    page = pageApprovedPass(batchQuery, tokenUser);
                    getApproPage(page, APPROVED_PASS);
                    break;
                case APPROVED_REJECT:
                    // 查询已驳回
                    page = pageApprovedReject(batchQuery, tokenUser);
                    getApproPage(page, APPROVED_REJECT);
                    break;
                case APPROVE_NOTIFIER_ME:
                    // 查询抄送我
                    page = pageApproveNotifierMe(batchQuery, tokenUser);
                    getApproPage(page, APPROVE_NOTIFIER_ME);
                    break;
                default:
            }
        } else {
            // 查询全部
            page = pageAllApprove(batchQuery, tokenUser);
        }
        return page;
    }

    @Override
    public Page<TransferAccountsBatchListVO> pageAllApprove(TransferAccountsBatchQuery batchQuery, TokenUser tokenUser) {
        List<TransferAccountsBatchListVO> batchListVOS = listAllApprove(batchQuery, tokenUser);
        return PageKit.startPage(batchListVOS, batchQuery.getPageNo(), batchQuery.getPageSize());
    }

    @Override
    public List<TransferAccountsBatchListVO> listAllApprove(TransferAccountsBatchQuery batchQuery, TokenUser tokenUser) {
        // 全部的转账批次
        List<TransferAccountsBatchListVO> batchListVOS = select(batchQuery, tokenUser.getSiteId());

        // 查询各个状态的审批
        UserFlowApproQuery userFlowApproQuery = new UserFlowApproQuery();
        // 用工单位id设置为和前端约定的固定字符串
        userFlowApproQuery.setCompanyId(HrConstants.COMPANY_APPROVE_ONE_KEY_PAY);

        // 查询待我审批当前人的审批
        List<UserApproNodeInfoVO> approvingList = approProviderService
                .listUserApprovesWaitApproveByMe(userFlowApproQuery, tokenUser);
        List<TransferAccountsBatchListVO> list1 = listParseData(batchListVOS, approvingList, APPROVING);
        // 查询已通过的审批
        List<UserApproNodeInfoVO> approvePassList = approProviderService
                .listPassedApproves(userFlowApproQuery, tokenUser);
        List<TransferAccountsBatchListVO> list2 = listParseData(batchListVOS, approvePassList, APPROVED_PASS);
        // 查询已驳回的审批
        List<UserApproNodeInfoVO> approveRejectList = approProviderService
                .listRejectedApproves(userFlowApproQuery, tokenUser);
        List<TransferAccountsBatchListVO> list3 = listParseData(batchListVOS, approveRejectList, APPROVED_REJECT);
        // 查询抄送我的审批
        List<UserApproNodeInfoVO> approveNotifierMList = approProviderService
                .listUserApprosNotifierMe(userFlowApproQuery, tokenUser);
        List<TransferAccountsBatchListVO> list4 = listParseData(batchListVOS, approveNotifierMList, APPROVE_NOTIFIER_ME);
        list1.addAll(list2);
        list1.addAll(list3);
        list1.addAll(list4);
        return list1.stream()
                .filter(StreamKit.distinctByKey(TransferAccountsBatchListVO::getId))
                .sorted(Comparator.comparing(TransferAccountsBatchListVO::getApproveStatus))
                .collect(Collectors.toList());
    }

    @Override
    public List<TransferBuildDetailAddVO> buildAllInfo(Integer type, String companyId, TokenUser tokenUser) {
        List<TransferBuildDetailAddVO> transferBuildDetailAddVOList = new ArrayList<>();
        if (type == 1) {
            // 根据租户id查询全部转账人员库
            List<TransferAccountsPeopleDO> transferAccountsPeopleDOList = transferAccountsPeopleService
                    .selectBySiteId(tokenUser.getSiteId());
            if (CollectionUtils.isEmpty(transferAccountsPeopleDOList)) {
                return transferBuildDetailAddVOList;
            }
            for (TransferAccountsPeopleDO transferAccountsPeopleDO : transferAccountsPeopleDOList) {
                TransferBuildDetailAddVO transferBuildDetailAddVO = new TransferBuildDetailAddVO();
                transferBuildDetailAddVO.setId(transferAccountsPeopleDO.getId());
                transferBuildDetailAddVO.setType(type);
                transferBuildDetailAddVOList.add(transferBuildDetailAddVO);
            }
        } else {
            // 查询花名册
            List<EmpInfoDO> empInfoDOList = empInfoService.onJobEmpDOList(companyId, tokenUser);
            if (CollectionUtils.isEmpty(empInfoDOList)) {
                return transferBuildDetailAddVOList;
            }
            for (EmpInfoDO empInfoDO : empInfoDOList) {
                TransferBuildDetailAddVO transferBuildDetailAddVO = new TransferBuildDetailAddVO();
                transferBuildDetailAddVO.setId(empInfoDO.getId());
                transferBuildDetailAddVO.setType(type);
                transferBuildDetailAddVOList.add(transferBuildDetailAddVO);
            }
        }
        return transferBuildDetailAddVOList;
    }

    @Override
    public List<TransferAccountsBatchDO> selectByOrderTitle(String orderTitle, String siteId) {
        Example example = new Example(TransferAccountsBatchDO.class);
        example.and()
                .andEqualTo("siteId", siteId)
                .andEqualTo("orderTitle", orderTitle)
                .andEqualTo("valid", true);
        return transferAccountsBatchDAO.selectByExample(example);
    }

    @Override
    public int updateZfbAppAuthToken(String code, String pid, TokenUser tokenUser) {
        // 根据pid查询转账账户表  没有查询到直接报错
        TransferAccountInfoDO transferAccountInfoDO = transferAccountInfoService.selectByTypeAndAccount(
                TransferAccountsTypeEnum.ZFB.getValue(), pid);
        ParamException.notNull(transferAccountInfoDO, "绑定失败，pid对应的账号不存在或已删除，请刷新重试!");
        // 获取appAuthToken
        CertAlipayRequest certAlipayRequest = this.buildCertAlipayRequest();
        StringBuilder selectPid = new StringBuilder();
        String appAuthToken = Alipay.alipayOpenAuthTokenApp(code, certAlipayRequest, selectPid);
        ParamException.notNull(appAuthToken, "根据code生成appAuthToken失败，请联系管理员查看失败原因!");
        if (!selectPid.toString().equals(pid)) {
            throw new ParamException("绑定失败，请登录对应的支付宝(账号名：" +
                    transferAccountInfoDO.getName() + ")进行授权");
        }
        cache.delKey(CacheKeyKit.getSiteAppAuthTokenCacheKey(selectPid.toString()));
        cache.set(CacheKeyKit.getSiteAppAuthTokenCacheKey(selectPid.toString()), appAuthToken);
        transferAccountInfoDO.setZfbToken(appAuthToken);
        return transferAccountInfoService.updateByIdSelective(transferAccountInfoDO, transferAccountInfoDO.getSiteId(), tokenUser.getUserId());
    }

    /**
     * 为分页数据设置审批状态
     *
     * @param page          page对象
     * @param approveStatus 审批状态
     */
    private void getApproPage(Page<TransferAccountsBatchListVO> page, Integer approveStatus) {
        for (TransferAccountsBatchListVO entity : page.getEntities()) {
            entity.setApproveStatus(approveStatus);
        }
    }

    public static void main(String[] args) {
        String ss = "charset=UTF-8&amp;biz_content=%7B%22success_amount%22%3A%220.00%22%2C%22biz_scene%22%3A%22MESSAGE_SATF_BATCH_PAY%22%2C%22total_count%22%3A%229%22%2C%22batch_trans_id%22%3A%22202112209350065917%22%2C%22batch_status%22%3A%22FAIL%22%2C%22success_count%22%3A%220%22%2C%22out_batch_no%22%3A%228E9660E9BC8B4661BCDCAD56EE013985%22%2C%22product_code%22%3A%22BATCH_API_TO_ACC_NO_PWD%22%2C%22fail_amount%22%3A%229.00%22%2C%22total_amount%22%3A%229.00%22%2C%22origin_interface%22%3A%22alipay.fund.batch.uni.transfer%22%2C%22payer_user_id%22%3A%222088410300144849%22%2C%22fail_count%22%3A%229%22%7D&amp;utc_timestamp=1639987245627&amp;sign=EPWPdVJpw64NPKp8IQJqYfGwa8kRljcJVRRlQSQasn3s%2FZhzJrqHz5yKi%2F2ZLtTNi34xFP2tCF%2BZs8XvFUeh3I7UETLYNO6aJrZImpOEa%2FXcq5QEfp3LxgdPBW1r%2BIG8dOXi%2Bt%2BEMlM9magUMLfY6Pt1v2PN5j42jbG06%2FPkIQenuSerUXAiElFhikki8bqemGNjR2RX14g1qWPBWyk%2FVW0od5h6sGpZGQGCec3oi3gDSP%2FTUWvGpZ3lmu2z%2FOrSQxyzByChshbB1gj6MKSahfm4j4HzO%2BqJQVONQ4b0sQEsN0SNZjnixGk01quwRCmqjnI5qQApaQpm9x7zsaWa6Q%3D%3D&amp;app_id=2021002196647967&amp;version=1.1&amp;sign_type=RSA2&amp;notify_id=2021122000262160045056205544103372&amp;msg_method=alipay.fund.batch.order.changed";
        buildParams(ss);
    }


    private static Map<String, String> buildParams(String requestBodyStr) {
        Map<String, String> map = new HashMap<>(8);
        List<String> ss = ArrayKit.split(requestBodyStr, "&amp;");
        ss.forEach(s -> {
            List<String> sss = ArrayKit.split(s, "=");
            if (sss.size() > 1) {
                try {
                    map.put(URLDecoder.decode(sss.get(0), "UTF-8"), URLDecoder.decode(sss.get(1), "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    log.error("解析字符串出错");
                    throw new ParamException(e.toString());
                }
            }
        });
        return map;
    }


    /**
     * 根据查询参数，构建example
     *
     * @param transferAccountsBatchQuery 查询参数
     * @param siteId                     所属站点id
     * @return example
     */
    private Example buildExample(TransferAccountsBatchQuery transferAccountsBatchQuery, String siteId) {
        Example example = new Example(TransferAccountsBatchDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        if (transferAccountsBatchQuery.getSelectName() != null) {
            example.and()
                    .andLike("createUserName", "%" + transferAccountsBatchQuery.getSelectName() + "%")
                    .orLike("companyName", "%" + transferAccountsBatchQuery.getSelectName() + "%")
                    .orLike("orderTitle", "%" + transferAccountsBatchQuery.getSelectName() + "%");
        }
        if (transferAccountsBatchQuery.getCreateUserName() != null) {
            example.and().andLike("createUserName", "%" + transferAccountsBatchQuery.getCreateUserName() + "%");
        }
        if (transferAccountsBatchQuery.getCompanyId() != null) {
            example.and().andEqualTo("companyId", transferAccountsBatchQuery.getCompanyId());
        }
        if (CollectionKit.isNotEmpty(transferAccountsBatchQuery.getCompanyIdList())) {
            example.and()
                    .andIn("companyId", transferAccountsBatchQuery.getCompanyIdList());
        }
        if (transferAccountsBatchQuery.getType() != null) {
            example.and().andEqualTo("type", transferAccountsBatchQuery.getType());
        }
        if (transferAccountsBatchQuery.getSecondType() != null) {
            example.and().andEqualTo("secondType", transferAccountsBatchQuery.getSecondType());
        }
        if (transferAccountsBatchQuery.getThirdPartyInteriorBatchId() != null) {
            example.and().andEqualTo("thirdPartyInteriorBatchId", transferAccountsBatchQuery.getThirdPartyInteriorBatchId());
        }
        if (transferAccountsBatchQuery.getOrderTitle() != null) {
            example.and().andLike("orderTitle", "%" + transferAccountsBatchQuery.getOrderTitle() + "%");
        }
        if (transferAccountsBatchQuery.getSubjectStatus() != null) {
            example.and().andEqualTo("subjectStatus", transferAccountsBatchQuery.getSubjectStatus());
        }
        if (transferAccountsBatchQuery.getPayStatus() != null) {
            example.and().andEqualTo("payStatus", transferAccountsBatchQuery.getPayStatus());
        }
        if (transferAccountsBatchQuery.getIssueUserName() != null) {
            example.and().andEqualTo("issueUserName", transferAccountsBatchQuery.getIssueUserName());
        }
        if (transferAccountsBatchQuery.getIssueAccountName() != null) {
            example.and().andLike("issueAccountName", "%" + transferAccountsBatchQuery.getIssueAccountName() + "%");
        }
        if (transferAccountsBatchQuery.getPayType() != null) {
            example.and().andEqualTo("payType", transferAccountsBatchQuery.getPayType());
        }
        if (transferAccountsBatchQuery.getIsDispose() != null) {
            example.and().andEqualTo("isDispose", transferAccountsBatchQuery.getIsDispose());
        }
        if (transferAccountsBatchQuery.getGmtCreateStart() != null &&
                transferAccountsBatchQuery.getGmtCreateEnd() != null) {
            example.and().andBetween("gmtCreate", DateKit8.getStartTime(transferAccountsBatchQuery.getGmtCreateStart()),
                    DateKit8.getEndTime(transferAccountsBatchQuery.getGmtCreateEnd()));
        }
        if (CollectionKit.isNotEmpty(transferAccountsBatchQuery.getNotSubjectStatusList())) {
            example.and()
                    .andNotIn("subjectStatus", transferAccountsBatchQuery.getNotSubjectStatusList());
        }
        // 排序
        ExampleKit.setExampleOrder(example, transferAccountsBatchQuery.getOrders());
        return example;
    }
}




